/*
 * Copyright 2019 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_cau3_ble.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.cau3"
#endif

/*! Compile time sizeof() check */
#define BUILD_ASSURE(condition, msg) extern int msg[1 - 2 * (!(condition))] __attribute__((unused))

#define CAU3_SR_TKCS_INITRUN 0x00000000U
#define CAU3_SR_TKCS_RUN 0x00000100U
#define CAU3_SR_TKCS_DBGHALT 0x00000200U
#define CAU3_SR_TKCS_STOPNOERR 0x00000900U
#define CAU3_SR_TKCS_STOPERROR 0x00000A00U
#define CAU3_SR_TKCS_SECV 0x00000E00U
#define CAU3_SR_TKCS_EXTSECV 0x00000F00U

#define CAU3_DMEM_STK_BASE 0xfff003f8U
#define CAU3_DMEM_AES_RCON 0xfff00A00U

#define CAU3_TASK_SECV_INIT (0x00 * 4)
#define CAU3_TASK_STOPERROR (0x01 * 4)
#define CAU3_TASK_STOPNOERR (0x02 * 4)
#define CAU3_TASK_NULL (0x02 * 4)
#define CAU3_TASK_BLKLD_DMEM (0x03 * 4)
#define CAU3_TASK_LD_KEYCTX (0x04 * 4)
#define CAU3_TASK_LD_SP_KEYCTX (0x05 * 4)
#define CAU3_TASK_CLR_KEYCTX (0x06 * 4)
#define CAU3_TASK_LD_KEY (0x07 * 4)
#define CAU3_TASK_LD_IV (0x08 * 4)
#define CAU3_TASK_AES_KEY_SCH (0x09 * 4)
#define CAU3_TASK_AES_ENCRYPT (0x0A * 4)
#define CAU3_TASK_AES_DECRYPT (0x0B * 4)
#define CAU3_TASK_AES128_ENCRYPT (0x0C * 4)
#define CAU3_TASK_AES128_DECRYPT (0x0D * 4)
#define CAU3_TASK_AES128_CMAC (0x0E * 4)
#define CAU3_TASK_SHA256_INIT_STATE (0x0F * 4)
#define CAU3_TASK_SHA256_UPDATE (0x10 * 4)
#define CAU3_TASK_KEY_BLOB_UNWRAP (0x11 * 4)
#define CAU3_TASK_SHA1_HASH (0x12 * 4)
#define CAU3_TASK_SHA1_INIT_STATE (0x13 * 4)
#define CAU3_TASK_SHA512_INIT_STATE (0x14 * 4)
#define CAU3_TASK_SHA512_UPDATE (0x15 * 4)
#define CAU3_TASK_3DES_CHECK_PARITY (0x16 * 4)
#define CAU3_TASK_3DES_ENCRYPT (0x17 * 4)
#define CAU3_TASK_3DES_DECRYPT (0x18 * 4)
#define CAU3_TASK_CHA_POLY_ENCRYPT (0x19 * 4)
#define CAU3_TASK_CHA_POLY_DECRYPT (0x1A * 4)
#define CAU3_TASK_RPA_TABLE_SIZE (0x1B * 4)
#define CAU3_TASK_RPA_INSERT_KEY (0x1C * 4)
#define CAU3_TASK_RPA_REMOVE_KEY (0x1D * 4)
#define CAU3_TASK_RPA_TABLE_SEARCH (0x1E * 4)

#define CAU3_SEMA4_LOCK 0x80000000U
#define CAU3_SEMA4_RELEASE 0x00000000U
#define CAU3_SMOWNR_OWNED_BY_ME 0x00000001U
#define CAU3_SMOWNR_UNLOCKED 0x80000000U

/*! @brief keyContext structure in the CAU3's DataMemory */
typedef struct _cau3_key_context
{
    uint32_t keySize;    /*!< key size in bytes, 0 = invalid context */
    uint32_t streamSize; /*!< rfu-firmware; stream length in bytes */
    uint32_t *keySched;  /*!< rfu-firmware; ptr to expanded key schedule */
    uint32_t zeroFill;   /*!< zero (unused) to keep 0-mod-4 alignment */
    uint8_t iv[16];      /*!< initialization vector */
    uint8_t key[32];     /*!< key for 3des, aes[128,192,256] */
} cau3_key_context_t;

typedef enum _cau3_crypt {
    kCAU3_Encrypt = 0,
    kCAU3_Decrypt = 1,
} cau3_crypt_t;

#define cau3_memcpy memcpy
#define cau3_force_zero(ptr, len) (memset((ptr), 0, (len)))

/*! Internal states of the HASH creation process */
typedef enum _cau3_hash_algo_state {
    kCAU3_StateHashInit = 1u, /*!< Init state. */
    kCAU3_StateHashUpdate,    /*!< Update state. */
} cau3_hash_algo_state_t;

/*! multiple of 64-byte block represented as byte array of 32-bit words */
typedef union _cau3_hash_block {
    uint32_t w[CAU3_HASH_BLOCK_SIZE / 4]; /*!< array of 32-bit words */
    uint8_t b[CAU3_HASH_BLOCK_SIZE];      /*!< byte array */
} cau3_hash_block_t;

/*! internal cau3_hash context structure */
typedef struct _cau3_hash_ctx_internal
{
    cau3_hash_block_t blk;        /*!< memory buffer. only full blocks are written to CAU3 during hash/cmac updates */
    size_t blksz;                 /*!< number of valid bytes in memory buffer */
    cau3_hash_algo_t algo;        /*!< selected algorithm from the set of supported algorithms */
    cau3_hash_algo_state_t state; /*!< finite machine state of the hash software process */
    size_t fullMessageSize;       /*!< track message size during CAU3_HASH_Update(). The value is used for padding. */
    uint32_t runningHash[8];
} cau3_hash_ctx_internal_t;

/*!< SHA-1/SHA-2 digest length in bytes  */
enum _cau3_hash_digest_len
{
    kCAU3_OutLenSha1 = 20u,
    kCAU3_OutLenSha256 = 32u,
};

/*! 64-byte block represented as byte array of 16 32-bit words */
typedef union _cau3_sha_block {
    uint32_t w[64 / 4]; /*!< array of 32-bit words */
    uint8_t b[64];      /*!< byte array */
} cau3_sha_block_t;

/*! Full word representing the actual bit values for the CAU3 1.0 mode register. */
typedef uint32_t cau3_mode_t;

#define CAU3_MDPK_ALG_PKHA (0x80U) /*!< Bit field value for CAU3_MDPK_ALG: PKHA */
#define CAU3_MD_ALG_SHIFT 16

typedef enum _cau3_algorithm {
    kCAU3_AlgorithmPKHA = CAU3_MDPK_ALG_PKHA << CAU3_MD_ALG_SHIFT,
} cau3_algorithm_t;

/*! @brief CAU3 status flags */
enum _cau3_status_flag
{
    kCAU3_StatusPkhaBusy = 1U << CAU3_STA_PB_SHIFT,
    kCAU3_StatusDoneIsr = 1U << CAU3_STA_DI_SHIFT,
    kCAU3_StatusErrorIsr = 1U << CAU3_STA_EI_SHIFT,
    kCAU3_StatusPublicKeyPrime = 1U << CAU3_STA_PKP_SHIFT,
    kCAU3_StatusPublicKeyOpOne = 1U << CAU3_STA_PKO_SHIFT,
    kCAU3_StatusPublicKeyOpZero = 1U << CAU3_STA_PKZ_SHIFT,
    kCAU3_StatusAll = 0 | kCAU3_StatusDoneIsr | kCAU3_StatusErrorIsr | kCAU3_StatusPkhaBusy |
                      kCAU3_StatusPublicKeyPrime | kCAU3_StatusPublicKeyOpOne | kCAU3_StatusPublicKeyOpZero
};

/*! @brief CAU3 clear register */
typedef enum _cau3_clear_written {
    kCAU3_ClearMode = 1U << 0,
    kCAU3_ClearDataSize = 1U << 2,
    kCAU3_ClearPkhaSizeA = 1U << 12,
    kCAU3_ClearPkhaSizeB = 1U << 13,
    kCAU3_ClearPkhaSizeN = 1U << 14,
    kCAU3_ClearPkhaSizeE = 1U << 15,
    kCAU3_ClearAllSize = (int)kCAU3_ClearPkhaSizeA | kCAU3_ClearPkhaSizeB | kCAU3_ClearPkhaSizeN | kCAU3_ClearPkhaSizeE,
    kCAU3_ClearAll = (int)(kCAU3_ClearMode | kCAU3_ClearDataSize | kCAU3_ClearAllSize | 0)
} cau3_clear_written_t;

/*! @brief PKHA functions - arithmetic, copy/clear memory. */
typedef enum _cau3_pkha_func_t {
    kCAU3_PKHA_ClearMem = 1U,
    kCAU3_PKHA_ArithModAdd = 2U,         /*!< (A + B) mod N */
    kCAU3_PKHA_ArithModSub1 = 3U,        /*!< (A - B) mod N */
    kCAU3_PKHA_ArithModSub2 = 4U,        /*!< (B - A) mod N */
    kCAU3_PKHA_ArithModMul = 5U,         /*!< (A x B) mod N */
    kCAU3_PKHA_ArithModExp = 6U,         /*!< (A^E) mod N */
    kCAU3_PKHA_ArithModRed = 7U,         /*!< (A) mod N */
    kCAU3_PKHA_ArithModInv = 8U,         /*!< (A^-1) mod N */
    kCAU3_PKHA_ArithEccAdd = 9U,         /*!< (P1 + P2) */
    kCAU3_PKHA_ArithEccDouble = 10U,     /*!< (P2 + P2) */
    kCAU3_PKHA_ArithEccMul = 11U,        /*!< (E x P(A0,A1) */
    kCAU3_PKHA_ArithModR2 = 12U,         /*!< (R^2 mod N) */
    kCAU3_PKHA_ArithModRR = 13U,         /*!< (RERP mod N) */
    kCAU3_PKHA_ArithGcd = 14U,           /*!< GCD (A, N) */
    kCAU3_PKHA_ArithPrimalityTest = 15U, /*!< Miller-Rabin */
    kCAU3_PKHA_CopyMemSizeN = 16U,
    kCAU3_PKHA_CopyMemSizeSrc = 17U,
    kCAU3_PKHA_ArithModSqrt = 0x17U, /*!< (B0 x B0) mod N = A mod N */
    kCAU3_PKHA_ArithEcmMul = 0x4B,   /*!< (E x P[A0]) */
    kCAU3_PKHA_ArithEctAdd = 0x89,   /*!< (P[A0,A1] + P[B1,B2]) */
    kCAU3_PKHA_ArithEctMul = 0x8B,   /*!< (E x P[A0,A1]) */
} cau3_pkha_func_t;

/*! @brief Register areas for PKHA clear memory operations. */
typedef enum _cau3_pkha_reg_area {
    kCAU3_PKHA_RegA = 8U,
    kCAU3_PKHA_RegB = 4U,
    kCAU3_PKHA_RegE = 2U,
    kCAU3_PKHA_RegN = 1U,
    kCAU3_PKHA_RegAll = kCAU3_PKHA_RegA | kCAU3_PKHA_RegB | kCAU3_PKHA_RegE | kCAU3_PKHA_RegN,
} cau3_pkha_reg_area_t;

/*! @brief Quadrant areas for 2048-bit registers for PKHA copy memory
 * operations. */
typedef enum _cau3_pkha_quad_area_t {
    kCAU3_PKHA_Quad0 = 0U,
    kCAU3_PKHA_Quad1 = 1U,
    kCAU3_PKHA_Quad2 = 2U,
    kCAU3_PKHA_Quad3 = 3U,
} cau3_pkha_quad_area_t;

/*! @brief User-supplied (R^2 mod N) input or CAU3 should calculate. */
typedef enum _cau3_pkha_r2_t {
    kCAU3_PKHA_CalcR2 = 0U, /*!< Calculate (R^2 mod N) */
    kCAU3_PKHA_InputR2 = 1U /*!< (R^2 mod N) supplied as input */
} cau3_pkha_r2_t;

/*! @brief CAU3 PKHA parameters */
typedef struct _cau3_pkha_mode_params_t
{
    cau3_pkha_func_t func;
    cau3_pkha_f2m_t arithType;
    cau3_pkha_montgomery_form_t montFormIn;
    cau3_pkha_montgomery_form_t montFormOut;
    cau3_pkha_reg_area_t srcReg;
    cau3_pkha_quad_area_t srcQuad;
    cau3_pkha_reg_area_t dstReg;
    cau3_pkha_quad_area_t dstQuad;
    cau3_pkha_timing_t equalTime;
    cau3_pkha_r2_t r2modn;
} cau3_pkha_mode_params_t;

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*******************************************************************************
 * CAU3 Read-Only Data Constants and CryptoCore Code Image
 ******************************************************************************/

/*  in the cau3's private, local data memory, there is a section for read-only  */
/*  constants associated with AES, SHA-1, SHA-256, SHA-384 and SHA-512.  */
/*  the memory organization and layout of this section is defined as:  */
/*   */
/*  description            size dmem_base       description  */
/*  cau_dmem_aes_rcon        48  FFF00800       RO aes constants        10 x 32b  */
/*  cau_dmem_sha1_k          16  FFF00830       RO sha1 initial "k"      4 x 32b              */
/*  cau_dmem_sha1_init_h     32  FFF00840       RO sha1 initial state    5 x 32b  */
/*  cau_dmem_sha224_init_h   32  FFF00860       RO sha224 initial state  8 x 32b              */
/*  cau_dmem_sha256_init_h   32  FFF00880       RO sha256 initial state  8 x 32b              */
/*  cau_dmem_sha256_k       256  FFF008A0       RO sha256 initial "k"   64 x 32b  */
/*  cau_dmem_sha384_init_h   64  FFF009A0       RO sha384 initial state 16 x 32b  */
/*  cau_dmem_sha512_init_h   64  FFF009E0       RO sha512 initial state 16 x 32b  */
/*  cau_dmem_sha512_k       640  FFF00A20       RO sha512 initial "k"   160 x 32b  */
/*   */
/*  data size allocation are rounded up to be modulo 16 bytes as required.  */
/*   */

static const uint32_t s_cau3ReadOnlyConstants[] __attribute__((aligned(16))) = {
    /*  AES RCON[]  */
    0x01000000U, 0x02000000U, 0x04000000U, 0x08000000U, 0x10000000U, 0x20000000U, 0x40000000U, 0x80000000U, 0x1b000000U,
    0x36000000U, 0x00000000U, 0x00000000U, /*  zero fill for 0-mod-16 alignment  */

    /*  SHA1_K[]  */
    0x5a827999U, 0x6ed9eba1U, 0x8f1bbcdcU, 0xca62c1d6U,

    /*  SHA1_INIT_H[]  */
    0x67452301U, 0xefcdab89U, 0x98badcfeU, 0x10325476U, 0xc3d2e1f0U, 0x00000000U, 0x00000000U,
    0x00000000U, /*  zero fill for 0-mod-16 alignemnt  */

    /*  SHA224_INIT_H[]  */
    0xc1059ed8U, 0x367cd507U, 0x3070dd17U, 0xf70e5939U, 0xffc00b31U, 0x68581511U, 0x64f98fa7U, 0xbefa4fa4U,

    /*  SHA256_INIT_H[]  */
    /*  As described in FIPS PUB 180-4 "Secure Hash Standard", the initial hash value  */
    /*  for SHA-256 is obtained by taking the first thirty-two bits of the fractional  */
    /*  parts of the square roots of the first eight prime numbers.  */
    0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU, 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U,

    /*  SHA256_K[], also used as SHA224_K[]  */
    /*  As described in FIPS PUB 180-4 "Secure Hash Standard", SHA-224 & SHA-256 use  */
    /*  the same sequence of sixty-four constant 32-bit words (K[]), where the words  */
    /*  represent the first thirty-two bits of the fractional parts of the cube roots  */
    /*  of the first sixty-four prime numbers.  */

    0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U, 0xd807aa98U,
    0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U, 0xe49b69c1U, 0xefbe4786U,
    0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, 0x983e5152U, 0xa831c66dU, 0xb00327c8U,
    0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U, 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U,
    0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U, 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U,
    0xd6990624U, 0xf40e3585U, 0x106aa070U, 0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU,
    0x5b9cca4fU, 0x682e6ff3U, 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U,
    0xc67178f2U,

    /*  SHA384_INIT_H[]  */
    /*  8 x 64-bit words in little-endian format  */
    0xc1059ed8U, 0xcbbb9d5dU, 0x367cd507U, 0x629a292aU, 0x3070dd17U, 0x9159015aU, 0xf70e5939U, 0x152fecd8U, 0xffc00b31U,
    0x67332667U, 0x68581511U, 0x8eb44a87U, 0x64f98fa7U, 0xdb0c2e0dU, 0xbefa4fa4U, 0x47b5481dU,

    /*  SHA512_INIT_H[]  */
    /*  8 x 64-bit words in little-endian format  */
    0xf3bcc908U, 0x6a09e667U, 0x84caa73bU, 0xbb67ae85U, 0xfe94f82bU, 0x3c6ef372U, 0x5f1d36f1U, 0xa54ff53aU, 0xade682d1U,
    0x510e527fU, 0x2b3e6c1fU, 0x9b05688cU, 0xfb41bd6bU, 0x1f83d9abU, 0x137e2179U, 0x5be0cd19U,

    /*  SHA512_K[]  */
    /*  80 x 64-bit words in little-endian format  */
    0xd728ae22U, 0x428a2f98U, 0x23ef65cdU, 0x71374491U, 0xec4d3b2fU, 0xb5c0fbcfU, 0x8189dbbcU, 0xe9b5dba5U, 0xf348b538U,
    0x3956c25bU, 0xb605d019U, 0x59f111f1U, 0xaf194f9bU, 0x923f82a4U, 0xda6d8118U, 0xab1c5ed5U, 0xa3030242U, 0xd807aa98U,
    0x45706fbeU, 0x12835b01U, 0x4ee4b28cU, 0x243185beU, 0xd5ffb4e2U, 0x550c7dc3U, 0xf27b896fU, 0x72be5d74U, 0x3b1696b1U,
    0x80deb1feU, 0x25c71235U, 0x9bdc06a7U, 0xcf692694U, 0xc19bf174U, 0x9ef14ad2U, 0xe49b69c1U, 0x384f25e3U, 0xefbe4786U,
    0x8b8cd5b5U, 0x0fc19dc6U, 0x77ac9c65U, 0x240ca1ccU, 0x592b0275U, 0x2de92c6fU, 0x6ea6e483U, 0x4a7484aaU, 0xbd41fbd4U,
    0x5cb0a9dcU, 0x831153b5U, 0x76f988daU, 0xee66dfabU, 0x983e5152U, 0x2db43210U, 0xa831c66dU, 0x98fb213fU, 0xb00327c8U,
    0xbeef0ee4U, 0xbf597fc7U, 0x3da88fc2U, 0xc6e00bf3U, 0x930aa725U, 0xd5a79147U, 0xe003826fU, 0x06ca6351U, 0x0a0e6e70U,
    0x14292967U, 0x46d22ffcU, 0x27b70a85U, 0x5c26c926U, 0x2e1b2138U, 0x5ac42aedU, 0x4d2c6dfcU, 0x9d95b3dfU, 0x53380d13U,
    0x8baf63deU, 0x650a7354U, 0x3c77b2a8U, 0x766a0abbU, 0x47edaee6U, 0x81c2c92eU, 0x1482353bU, 0x92722c85U, 0x4cf10364U,
    0xa2bfe8a1U, 0xbc423001U, 0xa81a664bU, 0xd0f89791U, 0xc24b8b70U, 0x0654be30U, 0xc76c51a3U, 0xd6ef5218U, 0xd192e819U,
    0x5565a910U, 0xd6990624U, 0x5771202aU, 0xf40e3585U, 0x32bbd1b8U, 0x106aa070U, 0xb8d2d0c8U, 0x19a4c116U, 0x5141ab53U,
    0x1e376c08U, 0xdf8eeb99U, 0x2748774cU, 0xe19b48a8U, 0x34b0bcb5U, 0xc5c95a63U, 0x391c0cb3U, 0xe3418acbU, 0x4ed8aa4aU,
    0x7763e373U, 0x5b9cca4fU, 0xd6b2b8a3U, 0x682e6ff3U, 0x5defb2fcU, 0x748f82eeU, 0x43172f60U, 0x78a5636fU, 0xa1f0ab72U,
    0x84c87814U, 0x1a6439ecU, 0x8cc70208U, 0x23631e28U, 0x90befffaU, 0xde82bde9U, 0xa4506cebU, 0xb2c67915U, 0xbef9a3f7U,
    0xe372532bU, 0xc67178f2U, 0xea26619cU, 0xca273eceU, 0x21c0c207U, 0xd186b8c7U, 0xcde0eb1eU, 0xeada7dd6U, 0xee6ed178U,
    0xf57d4f7fU, 0x72176fbaU, 0x06f067aaU, 0xa2c898a6U, 0x0a637dc5U, 0xbef90daeU, 0x113f9804U, 0x131c471bU, 0x1b710b35U,
    0x23047d84U, 0x28db77f5U, 0x40c72493U, 0x32caab7bU, 0x15c9bebcU, 0x3c9ebe0aU, 0x9c100d4cU, 0x431d67c4U, 0xcb3e42b6U,
    0x4cc5d4beU, 0xfc657e2aU, 0x597f299cU, 0x3ad6faecU, 0x5fcb6fabU, 0x4a475817U, 0x6c44198cU,
    /*  CHACHA_K[]  */
    0x61707865U, 0x3320646eU, 0x79622d32U, 0x6b206574U};

static const uint32_t s_cau3ReadOnlyConstantsBytes = sizeof(s_cau3ReadOnlyConstants);

static const uint32_t s_cau3ImemImage[] __attribute__((aligned(16))) = {
    0x60C00000U, 0x54000040U, 0x54000020U, 0x608003E0U, 0x60800540U, 0x60800B80U, 0x608012C0U, 0x60801540U, 0x608018A0U,
    0x60801BC0U, 0x608049E0U, 0x60806540U, 0x60808100U, 0x54000040U, 0x54000040U, 0x60809180U, 0x608093E0U, 0x60801DA0U,
    0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U, 0x54000040U,
    0x6080C640U, 0x6080C7A0U, 0x6080CAC0U, 0x6080CDE0U, 0xC8FFE3DEU, 0x08CC1234U, 0x8C001200U, 0x8C001201U, 0x8C001202U,
    0x8C001203U, 0xCE002240U, 0xCE002242U, 0x60000434U, 0x840003DEU, 0x54000020U, 0xC8FFE3DEU, 0x5C001FE0U, 0x08800E31U,
    0x0CC81A31U, 0x2000A012U, 0x101FFE12U, 0x00404652U, 0x8C004200U, 0x9C001204U, 0x9C001205U, 0x9C001206U, 0x9C001207U,
    0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0x9C001200U, 0x9C001201U, 0x9C001202U, 0x9C001203U, 0x9C001204U,
    0x9C001205U, 0x9C001206U, 0x9C001207U, 0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0x08510252U, 0x84000240U,
    0x0880E000U, 0x3C000400U, 0x67800980U, 0x20000400U, 0xC4000240U, 0x3C000100U, 0x08408254U, 0x00800012U, 0x08CC1A31U,
    0x67800B20U, 0x3C000300U, 0x66800AE0U, 0x63800AC0U, 0x08501A52U, 0x08501A52U, 0x08501A52U, 0x70002D5FU, 0x5C1FFFE0U,
    0x5801FFE0U, 0x54000020U, 0xC8FFE3DEU, 0x5C001FE0U, 0x08800E31U, 0x0CC81A31U, 0x2000A012U, 0x101FFE12U, 0x00404652U,
    0x00804200U, 0xCE002240U, 0xCE002242U, 0x20D4D4C4U, 0x1014D4C4U, 0x20D4D4C5U, 0x1014D4C5U, 0xCE002244U, 0xCE002246U,
    0x24000100U, 0x24000121U, 0x24000142U, 0x24000163U, 0x24000184U, 0x240001A5U, 0x240001C6U, 0x240001E7U, 0x00040000U,
    0x00040021U, 0x00040042U, 0x00040063U, 0x00040084U, 0x000400A5U, 0x000400C6U, 0x000400E7U, 0xCE002240U, 0xCE002242U,
    0xCE002244U, 0xCE002246U, 0x08510252U, 0x84000240U, 0x0880E000U, 0x3C000400U, 0x678010C0U, 0x20000400U, 0xC4000240U,
    0x3C000100U, 0x08408254U, 0x00800012U, 0x08CC1A31U, 0x67801260U, 0x3C000300U, 0x66801220U, 0x63801200U, 0x08501A52U,
    0x08501A52U, 0x08501A52U, 0x70002D5FU, 0x5C1FFFE0U, 0x5801FFE0U, 0x54000020U, 0x08800E31U, 0x08C81A31U, 0x2000A012U,
    0x101FFE12U, 0x00404652U, 0x5C1FFFE0U, 0xCE002240U, 0xCE002242U, 0xCE002244U, 0xCE002246U, 0xCE002240U, 0xCE002242U,
    0xCE002244U, 0xCE002246U, 0x5C1FFFE0U, 0x5801FFE0U, 0x04807FFFU, 0x63801520U, 0x7080001FU, 0x54000020U, 0x0CCC7E48U,
    0x638015C0U, 0x088C7E52U, 0x28000032U, 0x08800E52U, 0x08C81A52U, 0x2000A013U, 0x101FFE13U, 0x00404A73U, 0xC4000271U,
    0x28000413U, 0x0C800E21U, 0x08CC0A31U, 0x63801720U, 0x28000031U, 0x3C000008U, 0x638017E0U, 0x8C001201U, 0xCC001261U,
    0x60001771U, 0x60801840U, 0x8C001201U, 0xDC001261U, 0x600017F1U, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x0CCC7E28U,
    0x63801920U, 0x088C7E31U, 0x28000031U, 0x08800E31U, 0x08C81A31U, 0x2000A212U, 0x101FFE12U, 0x00404652U, 0x8C001204U,
    0x8C001205U, 0x8C001206U, 0x8C001207U, 0x3C000008U, 0x63801AE0U, 0xCE002244U, 0xCE002246U, 0x60801B60U, 0xDC001244U,
    0xDC001245U, 0xDC001246U, 0xDC001247U, 0x5C1FFFE0U, 0x5801FFE0U, 0x54000020U, 0xC8FFE3DEU, 0x08800E31U, 0x08C81A20U,
    0x2000A014U, 0x101FFE14U, 0x00400294U, 0x84000292U, 0x3C000312U, 0x66801D40U, 0x63801D20U, 0x08501A52U, 0x08501A52U,
    0x08501A52U, 0x28000414U, 0x60802D40U, 0x0C8013C6U, 0x63801E40U, 0xC8FEB3DEU, 0xC40013DFU, 0x60801E80U, 0xC8FEC3DEU,
    0xC40013DFU, 0x00804A55U, 0x00804636U, 0x00804E77U, 0x084006F4U, 0x3C000094U, 0x63001F60U, 0x20000034U, 0x3C000055U,
    0x63802080U, 0x3C000095U, 0x63802080U, 0x3C000115U, 0x63802080U, 0x3C000095U, 0x67002780U, 0x60802700U, 0x08800EC6U,
    0x08C818C6U, 0x2000A00CU, 0x101FFE0CU, 0x0040198CU, 0x8C004198U, 0x08800E31U, 0x08C82226U, 0x2000C011U, 0x101FFE11U,
    0x00401A31U, 0x3C000318U, 0x66802280U, 0x638026C0U, 0x200001D2U, 0x608022A0U, 0x20000152U, 0x2001E313U, 0x101FFE13U,
    0x9C001200U, 0x9C001201U, 0x20000106U, 0x004078C6U, 0x008056AAU, 0x9C00120EU, 0x9C00120FU, 0xCE0020CEU, 0x6000238AU,
    0x200000C9U, 0x008056AAU, 0x08500527U, 0x01001EA7U, 0x004028E6U, 0x008C1821U, 0x08C80D48U, 0x800023C2U, 0x08401108U,
    0x800023C3U, 0x70006F3FU, 0x08501108U, 0xC00023C2U, 0x08401108U, 0xC00023C3U, 0x6000248AU, 0x60002429U, 0x8600018EU,
    0x008C01CBU, 0x008C05E6U, 0x0488196BU, 0x63802860U, 0x3C000115U, 0x63802780U, 0x00805EF1U, 0x700012DFU, 0x581FFFE0U,
    0x54000040U, 0x00805EF1U, 0x0080529DU, 0x700012DFU, 0x008077B1U, 0x700012DFU, 0x581FFFE0U, 0x54000040U, 0x00805EE7U,
    0x08800EF7U, 0x08C81AE6U, 0x2000A017U, 0x101FFE17U, 0x00401AF7U, 0x3C000055U, 0x638029E0U, 0x3C000095U, 0x638029E0U,
    0x20000407U, 0x60802A00U, 0x08C80EA7U, 0xCC0082E7U, 0x20000106U, 0x004078C6U, 0x008056AAU, 0x3C000115U, 0x63802B40U,
    0x8E0020C0U, 0xCE0022E0U, 0x60002ACAU, 0x60802D20U, 0x0850114AU, 0x8E0020C0U, 0xCE0022E0U, 0x60002B6AU, 0x08800E94U,
    0x08C81A87U, 0x2000A014U, 0x101FFE14U, 0x00401E94U, 0x2000008AU, 0x20000407U, 0xCC008287U, 0x8E0020C0U, 0xCE002280U,
    0x60002CCAU, 0x54000020U, 0x08C82236U, 0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x86000280U, 0x86002282U, 0x20014015U,
    0x101FFE15U, 0x00800004U, 0x00800425U, 0x00800846U, 0x00800C67U, 0xCE002220U, 0xCE002222U, 0x3C000192U, 0x66803D60U,
    0x63803D40U, 0x86004284U, 0x86006286U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U,
    0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U,
    0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U,
    0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U,
    0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U,
    0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U,
    0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U,
    0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U,
    0x008C14C6U, 0x008C18E7U, 0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U,
    0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x01800068U, 0x008C2084U, 0x008C10A5U, 0x008C14C6U, 0x008C18E7U,
    0xCE002224U, 0xCE002226U, 0x08D020E8U, 0x01800108U, 0xAC0012A8U, 0x008C2000U, 0x008C0021U, 0x008C0442U, 0x008C0863U,
    0xCE002220U, 0xCE002222U, 0x29FFE211U, 0x608048C0U, 0x54000040U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x08D02067U, 0x018000E7U, 0xAC0012A7U, 0x008C1C00U,
    0x008C0021U, 0x008C0442U, 0x008C0863U, 0xCE002220U, 0xCE002222U, 0x29FFEA11U, 0x04805294U, 0x63804920U, 0xC4FFA291U,
    0x04807FFFU, 0x63804980U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x08800E31U, 0x08C81A20U, 0x2000A012U,
    0x101FFE12U, 0x80000252U, 0x3C000312U, 0x66804B20U, 0x63804B00U, 0x08501A52U, 0x08501A52U, 0x08501A52U, 0x08C82236U,
    0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x0C800E04U, 0x63805340U, 0x3C000024U, 0x63805100U, 0x3C000044U, 0x63804EC0U,
    0x94FFF605U, 0x08C860A0U, 0x94000605U, 0x08CC20A6U, 0x00881800U, 0x08C860A1U, 0x94001605U, 0x08CC20A6U, 0x00881821U,
    0x08C860A2U, 0x94002605U, 0x08CC20A6U, 0x00881842U, 0x08C860A3U, 0x94003605U, 0x08CC20A6U, 0x00881863U, 0x608053C0U,
    0x94FFFA05U, 0x08C840A0U, 0x94000A05U, 0x08CC40A6U, 0x00881800U, 0x08C840A1U, 0x94001A05U, 0x08CC40A6U, 0x00881821U,
    0x08C840A2U, 0x94002A05U, 0x08CC40A6U, 0x00881842U, 0x08C840A3U, 0x94003A05U, 0x08CC40A6U, 0x00881863U, 0x608053C0U,
    0x94FFFE05U, 0x08C820A0U, 0x94000E05U, 0x08CC60A6U, 0x00881800U, 0x08C820A1U, 0x94001E05U, 0x08CC60A6U, 0x00881821U,
    0x08C820A2U, 0x94002E05U, 0x08CC60A6U, 0x00881842U, 0x08C820A3U, 0x94003E05U, 0x08CC60A6U, 0x00881863U, 0x608053C0U,
    0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0xAC001220U, 0xAC001221U, 0xAC001222U, 0xAC001223U, 0x08500A48U,
    0x08CC0508U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U,
    0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U,
    0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x60005488U, 0x01800000U, 0x01800021U, 0x01800042U,
    0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U,
    0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0xAC001220U, 0xAC001221U, 0xAC001222U, 0xAC001223U, 0x0C800E64U,
    0x63806400U, 0x3C000024U, 0x638060E0U, 0x3C000044U, 0x63805DC0U, 0x94FFF664U, 0x08CC2084U, 0x08C82084U, 0x08CC6005U,
    0x008810A5U, 0xD4FFF665U, 0x08C82005U, 0x08CC6024U, 0x008810A5U, 0xD4000665U, 0x08C82025U, 0x08CC6044U, 0x008810A5U,
    0xD4001665U, 0x08C82045U, 0x08CC6064U, 0x008810A5U, 0xD4002665U, 0x94003664U, 0x08C86084U, 0x08CC6084U, 0x08C82065U,
    0x008810A5U, 0xD4003665U, 0x60806480U, 0x94FFFA64U, 0x08CC4084U, 0x08C84084U, 0x08CC4005U, 0x008810A5U, 0xD4FFFA65U,
    0x08C84005U, 0x08CC4024U, 0x008810A5U, 0xD4000A65U, 0x08C84025U, 0x08CC4044U, 0x008810A5U, 0xD4001A65U, 0x08C84045U,
    0x08CC4064U, 0x008810A5U, 0xD4002A65U, 0x94003A64U, 0x08C84084U, 0x08CC4084U, 0x08C84065U, 0x008810A5U, 0xD4003A65U,
    0x60806480U, 0x94FFFE64U, 0x08CC6084U, 0x08C86084U, 0x08CC2005U, 0x008810A5U, 0xD4FFFE65U, 0x08C86005U, 0x08CC2024U,
    0x008810A5U, 0xD4000E65U, 0x08C86025U, 0x08CC2044U, 0x008810A5U, 0xD4001E65U, 0x08C86045U, 0x08CC2064U, 0x008810A5U,
    0xD4002E65U, 0x94003E64U, 0x08C82084U, 0x08CC2084U, 0x08C86065U, 0x008810A5U, 0xD4003E65U, 0x60806480U, 0xD4000260U,
    0xD4001261U, 0xD4002262U, 0xD4003263U, 0x04807FFFU, 0x638064E0U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U,
    0x08800E31U, 0x08C81A20U, 0x2000A012U, 0x101FFE12U, 0x80000252U, 0x3C000312U, 0x66806680U, 0x63806660U, 0x08501A52U,
    0x08501A52U, 0x08501A52U, 0x08C82236U, 0x2000C011U, 0x101FFE11U, 0x00405A31U, 0x0C800E04U, 0x63806EA0U, 0x3C000024U,
    0x63806C60U, 0x3C000044U, 0x63806A20U, 0x94FFF605U, 0x08C860A0U, 0x94000605U, 0x08CC20A6U, 0x00881800U, 0x08C860A1U,
    0x94001605U, 0x08CC20A6U, 0x00881821U, 0x08C860A2U, 0x94002605U, 0x08CC20A6U, 0x00881842U, 0x08C860A3U, 0x94003605U,
    0x08CC20A6U, 0x00881863U, 0x60806F20U, 0x94FFFA05U, 0x08C840A0U, 0x94000A05U, 0x08CC40A6U, 0x00881800U, 0x08C840A1U,
    0x94001A05U, 0x08CC40A6U, 0x00881821U, 0x08C840A2U, 0x94002A05U, 0x08CC40A6U, 0x00881842U, 0x08C840A3U, 0x94003A05U,
    0x08CC40A6U, 0x00881863U, 0x60806F20U, 0x94FFFE05U, 0x08C820A0U, 0x94000E05U, 0x08CC60A6U, 0x00881800U, 0x08C820A1U,
    0x94001E05U, 0x08CC60A6U, 0x00881821U, 0x08C820A2U, 0x94002E05U, 0x08CC60A6U, 0x00881842U, 0x08C820A3U, 0x94003E05U,
    0x08CC60A6U, 0x00881863U, 0x60806F20U, 0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0x08C81259U, 0x00406631U,
    0xA8003223U, 0xA8FFF222U, 0xA8FFF221U, 0xA8FFF220U, 0x08500A59U, 0x08CC0739U, 0x01940000U, 0x01840063U, 0x01840042U,
    0x01840021U, 0x01840000U, 0x8AFFE23CU, 0x8AFFE23AU, 0x018C7463U, 0x018C7042U, 0x018C6C21U, 0x018C6800U, 0x01940000U,
    0x01840063U, 0x01840042U, 0x01840021U, 0x01840000U, 0x8AFFE23CU, 0x8AFFE23AU, 0x018C7463U, 0x018C7042U, 0x018C6C21U,
    0x018C6800U, 0x60007039U, 0x01940000U, 0x01840063U, 0x01840042U, 0x01840021U, 0x01840000U, 0x8AFFE23CU, 0x8AFFE23AU,
    0x018C7463U, 0x018C7042U, 0x018C6C21U, 0x018C6800U, 0x01940000U, 0x01840063U, 0x01840042U, 0x01840021U, 0x01840000U,
    0xA8FFF223U, 0xA8FFF222U, 0xA8FFF221U, 0xA8FFF220U, 0x0C800E79U, 0x63807FA0U, 0x3C000039U, 0x63807C80U, 0x3C000059U,
    0x63807960U, 0x94FFF664U, 0x08CC2084U, 0x08C82084U, 0x08CC6005U, 0x008810A5U, 0xD4FFF665U, 0x08C82005U, 0x08CC6024U,
    0x008810A5U, 0xD4000665U, 0x08C82025U, 0x08CC6044U, 0x008810A5U, 0xD4001665U, 0x08C82045U, 0x08CC6064U, 0x008810A5U,
    0xD4002665U, 0x94003664U, 0x08C86084U, 0x08CC6084U, 0x08C82065U, 0x008810A5U, 0xD4003665U, 0x60808020U, 0x94FFFA64U,
    0x08CC4084U, 0x08C84084U, 0x08CC4005U, 0x008810A5U, 0xD4FFFA65U, 0x08C84005U, 0x08CC4024U, 0x008810A5U, 0xD4000A65U,
    0x08C84025U, 0x08CC4044U, 0x008810A5U, 0xD4001A65U, 0x08C84045U, 0x08CC4064U, 0x008810A5U, 0xD4002A65U, 0x94003A64U,
    0x08C84084U, 0x08CC4084U, 0x08C84065U, 0x008810A5U, 0xD4003A65U, 0x60808020U, 0x94FFFE64U, 0x08CC6084U, 0x08C86084U,
    0x08CC2005U, 0x008810A5U, 0xD4FFFE65U, 0x08C86005U, 0x08CC2024U, 0x008810A5U, 0xD4000E65U, 0x08C86025U, 0x08CC2044U,
    0x008810A5U, 0xD4001E65U, 0x08C86045U, 0x08CC2064U, 0x008810A5U, 0xD4002E65U, 0x94003E64U, 0x08C82084U, 0x08CC2084U,
    0x08C86065U, 0x008810A5U, 0xD4003E65U, 0x60808020U, 0xD4003263U, 0xD4002262U, 0xD4001261U, 0xD4000260U, 0x04807FFFU,
    0x63808080U, 0x7080001FU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x7080001FU, 0x08800E31U, 0x08C82236U, 0x2000C011U,
    0x101FFE11U, 0x00405A31U, 0x94000200U, 0x94001201U, 0x94002202U, 0x94003203U, 0xAC001220U, 0xAC001221U, 0xAC001222U,
    0xAC001223U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U,
    0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U,
    0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U,
    0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U,
    0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U,
    0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U,
    0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U,
    0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U,
    0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U,
    0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U, 0x01881C63U, 0x01800000U,
    0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0x8E002224U, 0x8E002226U, 0x01881000U, 0x01881421U, 0x01881842U,
    0x01881C63U, 0x01800000U, 0x01800021U, 0x01800042U, 0x01800063U, 0x01900000U, 0xAC001220U, 0xAC001221U, 0xAC001222U,
    0xAC001223U, 0xD4000260U, 0xD4001261U, 0xD4002262U, 0xD4003263U, 0x04807FFFU, 0x29FFEA11U, 0x63809120U, 0x7080001FU,
    0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U, 0x20015000U, 0x101FFE00U, 0x84000001U, 0x84001002U, 0x84002003U, 0x84003004U,
    0x84004005U, 0x84005006U, 0x84006007U, 0x84007008U, 0xD40003A1U, 0xD40013A2U, 0xD40023A3U, 0xD40033A4U, 0xD40043A5U,
    0xD40053A6U, 0xD40063A7U, 0xD40073A8U, 0x54000020U, 0xC8FF93DEU, 0xC40013DFU, 0x940003A1U, 0x940013A2U, 0x940023A3U,
    0x940033A4U, 0x940043A5U, 0x940053A6U, 0x940063A7U, 0x940073A8U, 0xC40043DBU, 0x0C800F7AU, 0x63809660U, 0x00506B7BU,
    0x2800009BU, 0x08C80F5AU, 0xC40053DAU, 0x088C7F5AU, 0x2800003AU, 0xC40063DAU, 0x2001541AU, 0x101FFE1AU, 0xC40033DDU,
    0xC40023DCU, 0x840043DFU, 0x0C800FFFU, 0x6380AF80U, 0x94FFF37FU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU,
    0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU,
    0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU,
    0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU,
    0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU,
    0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU, 0x00CC73FDU, 0x00887529U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x840053DCU, 0x00C873E9U, 0x9C00137FU, 0x840063DCU,
    0x00CC73FDU, 0x00887529U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x6080BD80U,
    0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U,
    0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU,
    0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U,
    0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U,
    0x8C001340U, 0x01E80100U, 0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x9C001369U, 0x8C001340U, 0x01E80100U,
    0x01E41520U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x2000019FU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U,
    0x01E41500U, 0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U,
    0x00400084U, 0x01EC0020U, 0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U, 0x00400084U,
    0x01EC0020U, 0x41FFFEFFU, 0x00406609U, 0x01E06169U, 0x8C001340U, 0x01E80120U, 0x01E41500U, 0x00400084U, 0x01EC0020U,
    0x41FFFEFFU, 0x6000BDBFU, 0x840033DDU, 0x940003A0U, 0x00400021U, 0x940013A0U, 0x00400042U, 0x940023A0U, 0x00400063U,
    0x940033A0U, 0x00400084U, 0x940043A0U, 0x004000A5U, 0x940053A0U, 0x004000C6U, 0x940063A0U, 0x004000E7U, 0x940073A0U,
    0x00400108U, 0xD40003A1U, 0xD40013A2U, 0xD40023A3U, 0xD40033A4U, 0xD40043A5U, 0xD40053A6U, 0xD40063A7U, 0xD40073A8U,
    0x29FFE01AU, 0x840023DCU, 0x600096BCU, 0x840013DFU, 0x840003DEU, 0x04807FFFU, 0x6380C5E0U, 0x7080001FU, 0x581FFFE0U,
    0x5C1FFFE0U, 0x54000020U, 0x04804210U, 0x6300C700U, 0x2001D611U, 0x101FFE11U, 0x84000231U, 0x54000020U, 0x20000011U,
    0x2001D610U, 0x101FFE10U, 0xC4000211U, 0x54000020U, 0x2001D608U, 0x101FFE08U, 0x84000108U, 0x3C000108U, 0x6680C860U,
    0x54000040U, 0x2000160AU, 0x0100290AU, 0x2001DB11U, 0x101FFE11U, 0x00402A31U, 0x28000028U, 0x2001D60AU, 0x101FFE0AU,
    0xC4000148U, 0x84003200U, 0x84002201U, 0x84001202U, 0x84000203U, 0x20000152U, 0x20000014U, 0x70002E1FU, 0x5C1FFFE0U,
    0x581FFFE0U, 0x54000020U, 0x2001D60AU, 0x101FFE0AU, 0x84000148U, 0x04584100U, 0x6700CB80U, 0x54000040U, 0x08500508U,
    0xC4000148U, 0x04584100U, 0x6380CDC0U, 0x2000160CU, 0x0100310AU, 0x2001DB09U, 0x101FFE09U, 0x0080252DU, 0x00402929U,
    0x0100320AU, 0x004029ADU, 0x200002CCU, 0x8E00212AU, 0xCE0021AAU, 0x6000CD2CU, 0x5C1FFFE0U, 0x581FFFE0U, 0x54000020U,
    0x2001D70BU, 0x101FFE0BU, 0x2000000AU, 0xD400016AU, 0xD400116AU, 0xD400216AU, 0xD4003170U, 0x2001DB09U, 0x101FFE09U,
    0x00802D70U, 0x2001D913U, 0x101FFE13U, 0x2001D608U, 0x101FFE08U, 0x84000108U, 0x2000000CU, 0x085053DEU, 0xC40003C8U,
    0xC40033D1U, 0xC40043DFU, 0x04582180U, 0x20001FF2U, 0x6600D2E0U, 0xC40023CCU, 0xC40013C9U, 0x00802531U, 0x700081BFU,
    0x9400326DU, 0x08C821ADU, 0x08CC21ADU, 0x840033CEU, 0x840023CCU, 0x045839A0U, 0x6380D2C0U, 0x2800002CU, 0x840013C9U,
    0x28001609U, 0x840003C8U, 0x6080D060U, 0x00803192U, 0x840043DFU, 0x2800029EU, 0x04807FFFU, 0x6380D380U, 0x7080001FU,
    0x5C1FFFE0U, 0x581FFF60U, 0x54000020U, 0x00000000};

static const uint32_t s_cau3ImemBytes = sizeof(s_cau3ImemImage);

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

static status_t cau3_initialize_inst_memory(CAU3_Type *base, const uint32_t *cau3ImemImage, size_t cau3ImemBytes);
static status_t cau3_initialize_data_memory(CAU3_Type *base, cau3_task_done_t taskDone);
static status_t cau3_initialize_read_only_data_memory(CAU3_Type *base,
                                                      const uint32_t *cau3ReadOnlyConstants,
                                                      size_t cau3ReadOnlyConstantsBytes,
                                                      cau3_task_done_t taskDone);
static status_t cau3_load_key_context(CAU3_Type *base,
                                      cau3_key_context_t *cauKeyContext,
                                      cau3_key_slot_t keySlot,
                                      cau3_task_done_t taskDone);
static status_t cau3_load_key(
    CAU3_Type *base, const uint8_t *key, size_t keySize, uint32_t keySlot, cau3_task_done_t taskDone);
static status_t cau3_pkha_clear_regabne(CAU3_Type *base, bool A, bool B, bool N, bool E);

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static status_t cau3_lock_semaphore(CAU3_Type *base);
static void cau3_release_semaphore(CAU3_Type *base);
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/

static status_t cau3_process_task_completion(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t taskCompletionStatus;

    taskCompletionStatus = kStatus_Fail; /* assume an error completion status */
    switch (((uint32_t)taskDone >> 16U) & 7U)
    {
        uint32_t tkcs;

        case 0: /* poll the cau3 status register */
            tkcs = base->SR & CAU3_SR_TKCS_MASK;
            while (tkcs == CAU3_SR_TKCS_RUN)
            {
                tkcs = base->SR & CAU3_SR_TKCS_MASK;
            };

            /* check the task completion status*/
            if (tkcs == CAU3_SR_TKCS_STOPNOERR)
            {
                taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            }

            break;

        case 2: /* task completion signaled by event_done */
            do
            {
                __WFE(); /* cpu is waiting for cau3 event_done */
                tkcs = base->SR & CAU3_SR_TKCS_MASK;
            } while (tkcs == CAU3_SR_TKCS_RUN);

            /* check the task completion status */
            if (tkcs == CAU3_SR_TKCS_STOPNOERR)
            {
                taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            }
            break;

        case 1: /* task completion signaled by irq */
        /* TEMP FIX - for boot ROM with IRQ task completion, simply return */
        case 4: /* task completion signaled by dma_req */
            /* processing here is complete */
            taskCompletionStatus = kStatus_Success; /* signal error-free completion status */
            break;

        default: /* undefined taskDone specifier defaults to kStatus_Fail */
            break;
    } /* end - switch (taskDone & 7U) */

    return (taskCompletionStatus);
}

/*!
 * @brief   Initialize the CAU3's Instruction Memory
 *
 * Initializes the CAU3, including configuring it to enable the execution of
 * crypto tasks, loading the CryptoCore's firmware image into the CAU3's
 * instruction memory, and then performing a simple read-verify of its contents.
 * NOTE: All the operations for this function are executed on the host processor.
 *
 * cau3_initialize_inst_memory
 * @param   cau3ImemImage - binary firmware image for CryptoCore
 * @param   cau3ImemBytes - size of the firmware image in bytes
 *
 * @retval  status from the readVerify check: CAU_[OK (=0), ERROR (!0)]
 *          if an error is signaled, the retval is 0xbad10000UU + i, where i is
 *          the first miscompare word index location
 */
static status_t cau3_initialize_inst_memory(CAU3_Type *base, const uint32_t *cau3ImemImage, size_t cau3ImemBytes)
{
    uint32_t i;

    /* enable the cau3 */
    base->CR = 0U;

    /* poll if/while the cau3 is running initialization */
    while ((base->SR & CAU3_SR_TKCS_MASK) == CAU3_SR_TKCS_INITRUN)
    {
    };

    /* check for error-free stop state */
    if ((base->SR & CAU3_SR_TKCS_MASK) != CAU3_SR_TKCS_STOPNOERR)
    {
        return (0xbad00000U + (base->SR & CAU3_SR_TKCS_MASK)); /* exit with error */
    }

    base->SR = CAU3_SR_TCIRQ_MASK; /* clear the TCIRQ interrupt flag */

    /* write the code hex image into the cau3's imem
     * initialize the memory cmd and address registers */
    base->DBGMCMD = 0xac000000U; /* wt=1, ia=1, imem=0 */
    base->DBGMADR = 0U;          /* imem starting address */
    for (i = 0; i < cau3ImemBytes / 4; i++)
    {
        base->DBGMDR = cau3ImemImage[i]; /* indirect write into cau3Imem */
    }

    /* read-verify the cau3 imem code image
     * initialize the memory cmd and address registers */
    base->DBGMCMD = 0x8c000000U; /* wt=0, ia=1, imem=0 */
    base->DBGMADR = 0U;          /* imem starting address */
    for (i = 0; i < cau3ImemBytes / 4; i++)
    {
        if (base->DBGMDR != cau3ImemImage[i]) /* indirect read from cau3Imem */
        {
            return (0xbad10000U + i); /* exit on miscompare */
        }
    }

    /* this function does *not* disable reads/writes of the cau3 local memories
     * but, this operation is needed to "secure" (i.e., make private) the cau3
     * local memories */

    return 0U;
}

/*!
 * @brief   Initializes the CAU3's entire private Data Memory
 *
 * Initialize the CAU3's data memory, and then perform a read-verify versus a
 * precalculated "pseudo-hash" value.
 *
 * cau3_initialize_data_memory
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status from the readVerify check: CAU_[OK, ERROR]
 */
static status_t cau3_initialize_data_memory(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 "security violation + data initialization" task */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                 /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_SECV_INIT; /* call cau_secv_init() */
    base->CC_CMD = taskDone;           /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}

/*!
 * @brief   Copies read-only constants from sysMemory to CAU3's DataMemory
 *
 * Initialize the read-only constants in the CAU3's data memory. This includes
 * the AES constants (RCON) and most of the constants used in the hash functions.
 * The constants associated with SHA-512 are NOT included and must be loaded
 * separately.
 *
 * cau3_initialize_read_only_data_memory
 * @param   cauReadOnlyConstants - sysMemory table of constants needed by CAU3
 * @param   cauReadOnlyConstantsSize - size of read-only constants in bytes
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_initialize_read_only_data_memory(CAU3_Type *base,
                                                      const uint32_t *cau3ReadOnlyConstants,
                                                      size_t cau3ReadOnlyConstantsBytes,
                                                      cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 "initialize dmem read-only constants" task */
    base->CC_R[16] = (uint32_t)s_cau3ReadOnlyConstants; /* pReadOnlyConstants */
    base->CC_R[17] = s_cau3ReadOnlyConstantsBytes;      /* byte count (0-mod-16) */
    base->CC_R[18] = CAU3_DMEM_AES_RCON;                /* pDMEM_AES_RCON constants base */
    base->CC_R30 = CAU3_DMEM_STK_BASE;                  /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                                  /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_BLKLD_DMEM;                 /* call cau_block_load_dmem task */
    base->CC_CMD = taskDone;                            /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}

status_t CAU3_MakeMemsPrivate(CAU3_Type *base, cau3_task_done_t taskDone)
{
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    uint32_t completionStatus;
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif /* FSL_CAU3_USE_HW_SEMA */

    /* making the xMEMs private involves setting DBGCSR[DDBGMC] = 1 */
    base->DBGCSR = CAU3_DBGCSR_DDBGMC_MASK; /* set DBGCSR[DDBGMC] */

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return kStatus_Success;
}

/*!
 * @brief   Execute a CAU3 null task to "establish ownership" by host processor
 *
 * Execute a null task to claim ownership of the CAU3 by the host processor.
 * This is required for correct IRQ, EVT and DMA_REQ signaling by subsequent
 * PKHA operations. The CryptoCore task executes one instruction - a "stop".
 *
 * cau3_execute_null_task
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_execute_null_task(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    /* execute the cau3 null task */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /* initialize stack pointer (sp) */
    base->CC_R31 = 0;                  /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_NULL;      /* call cau_null() */
    base->CC_CMD = taskDone;           /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
    return (completionStatus);
}

/*!
 * @brief   Load a key into a key context
 *
 * Loads up to 32-byte key into the specified key slot.
 * There is support for a maximum of 4 key slots.
 * This does not do AES key expansion (as in cau3_load_key_context() case) so we use this one for loading TDES keys.
 *
 * @param   key is the key pointer, ALIGNED ON A 0-MOD-4 ADDRESS
 * @param   keySize is the size in bytes of the key
 * @param   keySlot is the destination key context
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_load_key(
    CAU3_Type *base, const uint8_t *key, size_t keySize, uint32_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load initialization vector into key context" task  */
    base->CC_R[16] = (uintptr_t)key;   /*  pKey  */
    base->CC_R[17] = keySize;          /*  IV size  */
    base->CC_R[18] = keySlot;          /*  keySlot  */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                 /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_LD_KEY;    /*  call cau_load_key()  */
    base->CC_CMD = taskDone;           /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_AES_Encrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t plaintext[16], uint8_t ciphertext[16])
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    taskDone = handle->taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_encrypt_ecb" task */
    base->CC_R[16] = (uint32_t)plaintext;  /* pPlainText */
    base->CC_R[17] = handle->keySlot;      /* keySlot */
    base->CC_R[19] = (uint32_t)ciphertext; /* pCipherText */
    base->CC_R30 = CAU3_DMEM_STK_BASE;     /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                     /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_AES_ENCRYPT;   /* call cau_aes_encrypt() */
    base->CC_CMD = taskDone;               /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_AES_Decrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t ciphertext[16], uint8_t plaintext[16])
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    taskDone = handle->taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_decrypt_ecb" task */
    base->CC_R[16] = (uint32_t)ciphertext; /* pCipherText */
    base->CC_R[17] = handle->keySlot;      /* keySlot */
    base->CC_R[19] = (uint32_t)plaintext;  /* pPlainText */
    base->CC_R30 = CAU3_DMEM_STK_BASE;     /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                     /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_AES_DECRYPT;   /* call cau_aes_decrypt() */
    base->CC_CMD = taskDone;               /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

static void cau3_xor_buf(uint8_t *buf, const uint8_t *mask, uint32_t count)
{
    uint32_t i;

    for (i = 0; i < count; i++)
        *(buf + i) ^= (uint8_t)*(mask + i);
}

static void cau3_aes_ctr_inc(uint8_t *B, uint32_t lenSz)
{
    uint32_t i;
    uint8_t *ptr;

    for (i = 0; i < lenSz; i++)
    {
        ptr = B + CAU3_AES_BLOCK_SIZE - 1 - i;
        *ptr += (uint8_t) 1;
        if ((uint8_t) *ptr != 0)
            return;
    }
}

/* CAU3_Type *base, cau3_handle_t *handle, const uint8_t plaintext[16], uint8_t ciphertext[16] */
static status_t cau3_aes_ecb_encrypt(CAU3_Type *base,
                                     cau3_key_slot_t keySlot,
                                     const uint8_t *inBlock,
                                     uint8_t *outBlock)
{
    status_t completionStatus;
    cau3_handle_t handle;

    handle.keySlot = keySlot;
    handle.taskDone = kCAU3_TaskDonePoll;

    completionStatus = CAU3_AES_Encrypt(base, &handle, inBlock, outBlock);
    if (completionStatus != kStatus_Success)
        cau3_force_zero(outBlock, CAU3_AES_BLOCK_SIZE);

    return completionStatus;
}

static status_t cau3_aes_ccm_auth(CAU3_Type *base,
                                  cau3_key_slot_t keySlot,
                                  const uint8_t *data,
                                  uint32_t len,
                                  uint8_t *x)
{
    uint32_t last = len % CAU3_AES_BLOCK_SIZE, i;
    status_t completionStatus = kStatus_Success;

    for (i = 0; i < len / CAU3_AES_BLOCK_SIZE; i++)
    {
        /* X_i+1 = E(K, X_i XOR B_i) */
        cau3_xor_buf(x, data, CAU3_AES_BLOCK_SIZE);
        data += CAU3_AES_BLOCK_SIZE;
        completionStatus = cau3_aes_ecb_encrypt(base, keySlot, x, x);
        if (completionStatus != kStatus_Success)
            return (completionStatus);
    }

    if (last)
    {
        /* XOR zero-padded last block */
        for (i = 0; i < last; i++)
            x[i] ^= (uint8_t) *data++;
        completionStatus = cau3_aes_ecb_encrypt(base, keySlot, x, x);
        if (completionStatus != kStatus_Success)
            return (completionStatus);
    }

    return (completionStatus);
}

static status_t cau3_aes_ccm_auth_start(CAU3_Type *base,
                                        cau3_key_slot_t keySlot,
                                        uint32_t authTagSz,
                                        uint32_t L,
                                        const uint8_t *nonce,
                                        const uint8_t *authIn,
                                        uint32_t authInSz,
                                        uint32_t inSz,
                                        uint8_t *x)
{
    uint32_t wordSz = (uint32_t)sizeof(uint32_t);
    status_t completionStatus = kStatus_Success;
    uint32_t authLenSz, remainder, i;
    uint8_t b[CAU3_AES_BLOCK_SIZE];
    uint8_t *bPtr = (uint8_t *)&b[0];
    uint8_t mask = 0xFF;

    /* Authentication */
    /* B_0: Flags | Nonce N | l(m) */
    b[0] = authInSz ? 0x40 : 0 /* Adata */;
    b[0] |= (((authTagSz - 2) / 2) /* M' */<< 3);
    b[0] |= (L - 1) /* L' */;
    cau3_memcpy(&b[1], nonce, 15 - L);

    for (i = 0; i < L; i++)
    {
        if (mask && i >= wordSz)
            mask = 0x00;
        b[CAU3_AES_BLOCK_SIZE - 1 - i] = (uint8_t)((inSz >> ((8 * i) & mask))
                & mask);
    }

    /* X_1 = E(K, B_0) */
    completionStatus = cau3_aes_ecb_encrypt(base, keySlot, bPtr, x);
    if (completionStatus != kStatus_Success)
        return (completionStatus);

    if (!authInSz)
        return 0;

    /* encode the length in */
    if (authInSz <= 0xFEFF)
    {
        authLenSz = 2;
        x[0] ^= (uint8_t) ((authInSz & 0xFF00) >> 8);
        x[1] ^= (uint8_t) (authInSz & 0x00FF);
    }
    else if (authInSz <= 0xFFFFFFFF)
    {
        authLenSz = 6;
        x[0] ^= (uint8_t) 0xFF;
        x[1] ^= (uint8_t) 0xFE;
        x[2] ^= (uint8_t) ((authInSz & 0xFF000000) >> 24);
        x[3] ^= (uint8_t) ((authInSz & 0x00FF0000) >> 16);
        x[4] ^= (uint8_t) ((authInSz & 0x0000FF00) >> 8);
        x[5] ^= (uint8_t) (authInSz & 0x000000FF);
    }
    /* Note, the protocol handles auth data up to 2^64, but we are
     * using 32-bit sizes right now, so the bigger data isn't handled
     * else if (inSz <= 0xFFFFFFFFFFFFFFFF) {} */
    else
        return (completionStatus);

    /* start fill out the rest of the first block */
    remainder = CAU3_AES_BLOCK_SIZE - authLenSz;
    if (authInSz >= remainder)
    {
        /* plenty of bulk data to fill the remainder of this block */
        cau3_xor_buf(x + authLenSz, authIn, remainder);
        authInSz -= remainder;
        authIn += remainder;
    }
    else
    {
        /* not enough bulk data, copy what is available, and pad zero */
        cau3_xor_buf(x + authLenSz, authIn, authInSz);
        authInSz = 0;
    }

    completionStatus = cau3_aes_ecb_encrypt(base, keySlot, x, x);
    if (completionStatus != kStatus_Success)
        return (completionStatus);

    if (authInSz > 0)
    {
        completionStatus = cau3_aes_ccm_auth(base, keySlot, authIn, authInSz, x);
        if (completionStatus != kStatus_Success)
            return (completionStatus);
    }

    return (completionStatus);
}

static status_t cau3_aes_ccm_encr(CAU3_Type *base,
                                  cau3_key_slot_t keySlot,
                                  uint32_t L,
                                  const uint8_t *in,
                                  uint32_t inSz,
                                  uint8_t *out,
                                  uint8_t * a)
{
    uint32_t last = inSz % CAU3_AES_BLOCK_SIZE, i;
    status_t completionStatus = kStatus_Success;

    for (i = 0; i < L; i++)
        a[CAU3_AES_BLOCK_SIZE - 1 - i] = (uint8_t)0;

    /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
    for (i = 1; i <= (inSz / CAU3_AES_BLOCK_SIZE); i++)
    {
        cau3_aes_ctr_inc(a, L);
        /* S_i = E(K, A_i) */
        completionStatus = cau3_aes_ecb_encrypt(base, keySlot, a, out);
        if (completionStatus != kStatus_Success)
            return (completionStatus);

        cau3_xor_buf(out, in,CAU3_AES_BLOCK_SIZE);
        out += CAU3_AES_BLOCK_SIZE;
        in += CAU3_AES_BLOCK_SIZE;
    }

    if (last)
    {
        cau3_aes_ctr_inc(a, L);
        completionStatus = cau3_aes_ecb_encrypt(base, keySlot, a, out);
        if (completionStatus != kStatus_Success)
            return (completionStatus);

        /* XOR zero-padded last block */
        for (i = 0; i < last; i++)
            *out++ ^= (uint8_t) *in++;
    }

    return (completionStatus);
}

static void cau3_aes_ccm_encr_start(uint32_t L, const uint8_t *nonce, uint8_t *a)
{
    /* A_i = Flags | Nonce N | Counter i */
    a[0] = L - 1; /* Flags = L' */
    cau3_memcpy(&a[1], nonce, 15 - L);
}

static status_t cau3_aes_ccm_encr_auth(CAU3_Type *base,
                                       cau3_key_slot_t keySlot,
                                       uint32_t L,
                                       uint32_t authTagSz,
                                       uint8_t *x,
                                       uint8_t *a,
                                       uint8_t *authTag)
{
    uint32_t i;
    uint8_t tmp[CAU3_AES_BLOCK_SIZE];

    /* U = T XOR S_0; S_0 = E(K, A_0) */
    for (i = 0; i < L; i++)
        a[CAU3_AES_BLOCK_SIZE - 1 - i] = (uint8_t) 0;

    if (cau3_aes_ecb_encrypt(base, keySlot, a, &tmp[0]))
    {
        cau3_force_zero(&tmp[0], CAU3_AES_BLOCK_SIZE);
        return kStatus_Fail;
    }

    for (i = 0; i < authTagSz; i++)
        authTag[i] = (uint8_t) x[i] ^ (uint8_t) tmp[i];

    return kStatus_Success;
}
static status_t cau3_aes_ccm_decr_auth(CAU3_Type *base,
                                       cau3_key_slot_t keySlot,
                                       uint32_t L,
                                       uint32_t authTagSz,
                                       uint8_t *a,
                                       const uint8_t *authTag,
                                       uint8_t *t)
{
    uint32_t i;
    uint8_t tmp[CAU3_AES_BLOCK_SIZE];

    /* U = T XOR S_0; S_0 = E(K, A_0) */
    for (i = 0; i < L; i++)
        a[CAU3_AES_BLOCK_SIZE - 1 - i] = (uint8_t) 0;

    if (cau3_aes_ecb_encrypt(base, keySlot, a, &tmp[0]))
    {
        cau3_force_zero(&tmp[0], CAU3_AES_BLOCK_SIZE);
        return kStatus_Fail;
    }

    for (i = 0; i < authTagSz; i++)
        t[i] = (uint8_t) authTag[i] ^ (uint8_t) tmp[i];

    return kStatus_Success;
}

static inline void cau3_aes_ccm_clear_mem(uint8_t *A, uint8_t *B, uint32_t len)
{
    cau3_force_zero(A, len);
    cau3_force_zero(B, len);
}

static status_t cau3_aes_ccm_encrypt(CAU3_Type *base,
                                     cau3_key_slot_t keySlot,
                                     const uint8_t *in,
                                     uint32_t inSz,
                                     uint8_t *out,
                                     const uint8_t *nonce,
                                     uint32_t nonceSz,
                                     const uint8_t *authIn,
                                     uint32_t authInSz,
                                     uint8_t *authTag,
                                     uint32_t authTagSz)
{
    const uint32_t L = CAU3_AES_BLOCK_SIZE - 1 - nonceSz;
    uint8_t x[CAU3_AES_BLOCK_SIZE];
    uint8_t *xPtr = (uint8_t*) &x[0];
    uint8_t a[CAU3_AES_BLOCK_SIZE];
    uint8_t *aPtr = (uint8_t*) &a[0];
    status_t completionStatus;

    /* sanity check on arguments */
    if (nonceSz < 7 || nonceSz > 13 || authTagSz < 4 || authTagSz > 16)
        return kStatus_InvalidArgument;

    completionStatus = cau3_aes_ccm_auth_start(base, keySlot, authTagSz, L, nonce,
                                               authIn, authInSz, inSz, xPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    completionStatus = cau3_aes_ccm_auth(base, keySlot, in, inSz, xPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    /* Encryption */
    cau3_aes_ccm_encr_start(L, nonce, aPtr);

    completionStatus = cau3_aes_ccm_encr(base, keySlot, L, in, inSz, out, aPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    completionStatus = cau3_aes_ccm_encr_auth(base, keySlot, L, authTagSz, xPtr, aPtr, authTag);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);

    return (completionStatus);
}

static int cau3_aes_ccm_decrypt(CAU3_Type *base,
                                cau3_key_slot_t keySlot,
                                uint8_t *out,
                                const uint8_t *in,
                                uint32_t inSz,
                                const uint8_t *nonce,
                                uint32_t nonceSz,
                                const uint8_t *authTag,
                                uint32_t authTagSz,
                                uint8_t *authPassed,
                                const uint8_t *authIn,
                                uint32_t authInSz)
{
    const uint32_t L = CAU3_AES_BLOCK_SIZE - 1 - nonceSz;
    uint8_t x[CAU3_AES_BLOCK_SIZE];
    uint8_t *xPtr = (uint8_t*) &x[0];
    uint8_t a[CAU3_AES_BLOCK_SIZE];
    uint8_t *aPtr = (uint8_t*) &a[0];
    uint8_t t[CAU3_AES_BLOCK_SIZE];
    uint8_t *tPtr = (uint8_t*) &t[0];
    status_t completionStatus;

    /* sanity check on arguments */
    if (nonceSz < 7 || nonceSz > 13 || authTagSz < 4 || authTagSz > 16)
        return kStatus_InvalidArgument;

    /* Decryption */
    cau3_aes_ccm_encr_start(L, nonce, aPtr);

    completionStatus = cau3_aes_ccm_decr_auth(base, keySlot, L, authTagSz, aPtr, authTag, tPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        cau3_force_zero(tPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
    completionStatus = cau3_aes_ccm_encr(base, keySlot, L, in, inSz, out, aPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        cau3_force_zero(tPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    completionStatus = cau3_aes_ccm_auth_start(base, keySlot, authTagSz, L, nonce, authIn,
                                               authInSz, inSz, xPtr);
    if (completionStatus != kStatus_Success) {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        cau3_force_zero(tPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    completionStatus = cau3_aes_ccm_auth(base, keySlot, out, inSz, xPtr);
    if (completionStatus != kStatus_Success)
    {
        cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
        cau3_force_zero(tPtr, CAU3_AES_BLOCK_SIZE);
        return (completionStatus);
    }

    if (memcmp(xPtr, tPtr, authTagSz) != 0) {
        // If the authTag check fails, don't keep the decrypted data.
        memset(out, 0, inSz);
        *authPassed = 0;
        return (completionStatus);
    } else {
        *authPassed = 1;
    }

    cau3_aes_ccm_clear_mem(xPtr, aPtr, CAU3_AES_BLOCK_SIZE);
    cau3_force_zero(tPtr, CAU3_AES_BLOCK_SIZE);

    return (completionStatus);
}

status_t CAU3_AES_CCM_EncryptTag(CAU3_Type *base,
                                 cau3_handle_t *handle,
                                 const uint8_t *plainText,
                                 size_t plainTextSize,
                                 uint8_t *cipherText,
                                 const uint8_t *nonce,
                                 size_t nonceSize,
                                 const uint8_t *aad,
                                 size_t aadSize,
                                 uint8_t *authTag,
                                 size_t authTagSize)
{
    status_t completionStatus;

    completionStatus = cau3_aes_ccm_encrypt(base, handle->keySlot, plainText, plainTextSize, cipherText,
                                            nonce, nonceSize, aad, aadSize, authTag, authTagSize);
    if (completionStatus != kStatus_Success)
        CAU3_ForceError(base, handle->taskDone);
    else
        completionStatus = cau3_execute_null_task(base, handle->taskDone);

    return (completionStatus);
}

uint32_t CAU3_AES_CCM_DecryptTag(CAU3_Type *base,
                                 cau3_handle_t *handle,
                                 const uint8_t *cipherText,
                                 uint8_t *plainText,
                                 size_t plainTextSize,
                                 const uint8_t *nonce,
                                 size_t nonceSize,
                                 const uint8_t *aad,
                                 size_t aadSize,
                                 const uint8_t *authTag,
                                 size_t authTagSize)
{
    status_t completionStatus;
    uint8_t authPassed = 0;

    completionStatus = cau3_aes_ccm_decrypt(base, handle->keySlot, plainText, cipherText, plainTextSize,
                                            nonce, nonceSize, authTag, authTagSize, &authPassed, aad, aadSize);

    if (completionStatus != kStatus_Success)
        CAU3_ForceError(base, handle->taskDone);
    else
        completionStatus = cau3_execute_null_task(base, handle->taskDone);

    handle->micPassed = authPassed;
    return (completionStatus);
}

void CAU3_Init(CAU3_Type *base)
{
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* ungate clock */
    CLOCK_EnableClock(kCLOCK_Cau3);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    CLOCK_EnableClock(FSL_CAU3_SEMA42_CLOCK_NAME);
#endif /* FSL_CAU3_USE_HW_SEMA */
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

    base->CR = CAU3_CR_RSTSM4(1);
    base->CR = CAU3_CR_RSTSM4(2);
    cau3_initialize_inst_memory(base, s_cau3ImemImage, s_cau3ImemBytes);
    cau3_initialize_data_memory(base, kCAU3_TaskDonePoll);
    cau3_initialize_read_only_data_memory(base, s_cau3ReadOnlyConstants, s_cau3ReadOnlyConstantsBytes,
                                          kCAU3_TaskDonePoll);
    cau3_pkha_clear_regabne(base, true, true, true, true);
}

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static status_t cau3_lock_semaphore(CAU3_Type *base)
{
    uint32_t processorNumber = 0;

    /* cm4 will be 1, cm0+ will be 2 */
    /* For next SoC, the processor number shall be defined in the SoC header file */
#if __CORTEX_M == 0U
    processorNumber++;
#endif
    processorNumber++;

    while (processorNumber != SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, 1))
    {
        /* Wait for unlocked status. */
        while (SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE))
        {
        }

        /* Lock the gate. */
        SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE) = processorNumber;
    }

    return cau3_execute_null_task(base, kCAU3_TaskDonePoll);
}
#endif /* FSL_CAU3_USE_HW_SEMA */

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
static void cau3_release_semaphore(CAU3_Type *base)
{
    /* unlock the semaphore */
    SEMA42_GATEn(FSL_CAU3_SEMA42_BASE, FSL_CAU3_SEMA42_GATE) = 0;
}
#endif /* FSL_CAU3_USE_HW_SEMA */

/*!
 * @brief   Load a 64-byte "key context" into the CAU3's private data memory
 *
 * Load the key context into the private DMEM. This includes size and config
 * information, a 16-byte initialization vector and a key of size [8,16,24,32]
 * bytes (for DES or AES-[128,192,256]). There is support for 4 "key slots" with
 * slot 0 typically used for the system key encryption key (KEK).
 *
 * See the GENERAL COMMENTS for more information on the keyContext structure.
 *
 * NOTE: This function also performs an AES key expansion if a keySize > 8
 * is specified.
 *
 * cau3_load_key_context
 * @param   cauKeyContext is pointer to key structure in sysMemory
 * @param   keySlot is the destination key slot number [0-3]
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @return  status check from task completion: CAU_[OK, ERROR]
 */
static status_t cau3_load_key_context(CAU3_Type *base,
                                      cau3_key_context_t *cauKeyContext,
                                      cau3_key_slot_t keySlot,
                                      cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "load key context" task */
    base->CC_R[16] = (uint32_t)cauKeyContext; /* pKeyContext */
    base->CC_R[17] = keySlot;                 /* keySlot */
    base->CC_R30 = CAU3_DMEM_STK_BASE;        /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                        /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_LD_KEYCTX;        /* call cau_load_key_context() */
    base->CC_CMD = taskDone;                  /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_ForceError(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 null task  */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0;                  /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_STOPERROR;
    base->CC_CMD = taskDone; /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_LoadSpecialKeyContext(CAU3_Type *base, size_t keySize, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load special key context" task  */
    base->CC_R[16] = keySize;             /*  keySize [8,16,24,32]  */
    base->CC_R[17] = keySlot;             /*  keySlot  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;    /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                    /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_LD_SP_KEYCTX; /*  call cau_load_special_key_context()  */
    base->CC_CMD = taskDone;              /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_ClearKeyContext(CAU3_Type *base, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    uint32_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "clear key context" task  */
    base->CC_R[17] = keySlot;           /*  keySlot  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;  /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                  /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_CLR_KEYCTX; /*  call cau_clear_key_context()  */
    base->CC_CMD = taskDone;            /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_LoadKeyInitVector(CAU3_Type *base, const uint8_t *iv, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "load initialization vector into key context" task  */
    base->CC_R[16] = (uintptr_t)iv;    /*  pIv  */
    base->CC_R[17] = keySlot;          /*  keySlot  */
    base->CC_R30 = CAU3_DMEM_STK_BASE; /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                 /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_LD_IV;     /*  call cau_load_iv()  */
    base->CC_CMD = taskDone;           /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_AES_KeyExpansion(CAU3_Type *base, cau3_key_slot_t keySlot, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "aes_key_expansion" task */
    base->CC_R[17] = keySlot;            /* keySlot */
    base->CC_R30 = CAU3_DMEM_STK_BASE;   /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                   /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_AES_KEY_SCH; /* call cau_aes_key_sched() */
    base->CC_CMD = taskDone;             /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_AES_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    cau3_key_context_t cau3KeyCtx = {0};

    /* only work with aligned key[] */
    if (0x3U & (uintptr_t)key)
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 16 or 32. initial CAU3 firmware doesn't support 24 bytes. */
    if ((keySize != 16U) && (keySize != 32U))
    {
        return kStatus_InvalidArgument;
    }

    cau3KeyCtx.keySize = keySize;

    /* move the key by 32-bit words */
    int i = 0;
    while (keySize)
    {
        keySize -= sizeof(uint32_t);
        ((uint32_t *)((uintptr_t)cau3KeyCtx.key))[i] = ((uint32_t *)(uintptr_t)key)[i];
        i++;
    }

    return cau3_load_key_context(base, &cau3KeyCtx, handle->keySlot, handle->taskDone);
}

status_t CAU3_AES_Cmac(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *message, size_t size, uint8_t *mac)
{
    status_t completionStatus;

    /* mac must be 0-mod-4 aligned */
    if (0x3U & (uintptr_t)mac)
    {
        return kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "aes_cmac" task  */
    base->CC_R[16] = (uintptr_t)message; /*  pMessage  */
    base->CC_R[17] = handle->keySlot;    /*  keySlot  */
    base->CC_R[18] = size;               /*  messageSize  */
    base->CC_R[19] = (uintptr_t)mac;     /*  pMac  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;   /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                   /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_AES128_CMAC; /*  call cau_aes128_cmac()  */
    base->CC_CMD = handle->taskDone;     /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief Check validity of algoritm.
 *
 * This function checks the validity of input argument.
 *
 * @param algo Tested algorithm value.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_input_alg(cau3_hash_algo_t algo)
{
    if ((algo != kCAU3_Sha256) && (algo != kCAU3_Sha1))
    {
        return kStatus_InvalidArgument;
    }
    return kStatus_Success;
}

/*!
 * @brief Check validity of input arguments.
 *
 * This function checks the validity of input arguments.
 *
 * @param base CAU3 peripheral base address.
 * @param ctx Memory buffer given by user application where the CAU3_HASH_Init/CAU3_HASH_Update/CAU3_HASH_Finish store
 * context.
 * @param algo Tested algorithm value.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_input_args(CAU3_Type *base, cau3_hash_ctx_t *ctx, cau3_hash_algo_t algo)
{
    /* Check validity of input algorithm */
    if (kStatus_Success != cau3_hash_check_input_alg(algo))
    {
        return kStatus_InvalidArgument;
    }

    if ((NULL == ctx) || (NULL == base))
    {
        return kStatus_InvalidArgument;
    }

    return kStatus_Success;
}

/*!
 * @brief Check validity of internal software context.
 *
 * This function checks if the internal context structure looks correct.
 *
 * @param ctxInternal Internal context.
 * @param message Input message address.
 * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise.
 */
static status_t cau3_hash_check_context(cau3_hash_ctx_internal_t *ctxInternal, const uint8_t *message)
{
    if ((NULL == message) || (NULL == ctxInternal) || (kStatus_Success != cau3_hash_check_input_alg(ctxInternal->algo)))
    {
        return kStatus_InvalidArgument;
    }
    return kStatus_Success;
}

/*!
 * @brief   Initialize message digest output state for SHA-1 hash
 *
 * Initializes the message digest output state for a SHA-1 hash.
 *
 * @param   sha1State is message digest output in sysMemory in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha1InitializeOutput(CAU3_Type *base, uint32_t *sha1State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "sha1_init_output" task  */
    base->CC_R[29] = (uintptr_t)sha1State;   /*  pSha1State  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;       /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                       /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_SHA1_INIT_STATE; /*  call cau_sha1_init_state()  */
    base->CC_CMD = taskDone;                 /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Perform a SHA-1 hash function over a message of "n" 64-byte blocks
 *
 * Perform a SHA-1 hash function over a message of "n" 64-byte data blocks,
 * returning an 8-word message digest (aka "state"). The input message must
 * be padded appropriately as defined by the SHA-1 algorithm.
 *
 * @param   message is the uint8_t input message, any alignment
 * @param   numberOfBlocks is the message length as multiple of 64-byte blocks
 * @param   sha1State is uint32_t message digest output (state) in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

static status_t CAU3_Sha1Update(
    CAU3_Type *base, const uint8_t *message, uint32_t numberOfBlocks, uint32_t *sha1State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "sha1_update" task  */
    base->CC_R[27] = (uintptr_t)message;   /*  pMessage  */
    base->CC_R[28] = numberOfBlocks;       /*  n blocks  */
    base->CC_R[29] = (uintptr_t)sha1State; /*  output  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;     /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                     /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_SHA1_HASH;     /*  call cau_sha1_hash_n()  */
    base->CC_CMD = taskDone;               /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Initialize message digest output state for SHA-256 hash
 *
 * Initializes the message digest output state for a SHA-256 hash.
 *
 * CAU3_Sha256InitializeOutput
 * @param   sha256State is message digest output in sysMemory in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

status_t CAU3_Sha256InitializeOutput(CAU3_Type *base, uint32_t *sha256State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "sha256_init_output" task */
    base->CC_R[29] = (uint32_t)sha256State;    /* pSha256State */
    base->CC_R30 = CAU3_DMEM_STK_BASE;         /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                         /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_SHA256_INIT_STATE; /* call cau_sha256_init_state() */
    base->CC_CMD = taskDone;                   /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief   Perform a SHA-256 hash function over a message of "n" 64-byte blocks
 *
 * Perform a SHA-256 hash function over a message of "n" 64-byte data blocks,
 * returning an 8-word message digest (aka "state"). The input message must
 * be padded appropriately as defined by the SHA-256 algorithm.
 *
 * CAU_Sha256Update
 * @param   message is the uint8_t input message, LE, ANY ALIGNMENT
 * @param   numberOfBlocks is the message length as multiple of 64-byte blocks
 * @param   sha256State is uint32_t message digest output (state) in BE format
 * @param   taskDone indicates completion signal: CAU_[POLL, IRQ, EVENT, DMAREQ]
 *
 * @retval  status check from task completion: CAU_[OK, ERROR]
 */

status_t CAU3_Sha256Update(
    CAU3_Type *base, const uint8_t *message, uint32_t numberOfBlocks, uint32_t *sha256State, cau3_task_done_t taskDone)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /* execute the cau3 "sha256_update" task */
    base->CC_R[27] = (uint32_t)message;     /* pMessage */
    base->CC_R[28] = numberOfBlocks;        /* = (64*numberOfBlocks) bytes */
    base->CC_R[29] = (uint32_t)sha256State; /* pSha256State */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /* initialize stack pointer (sp) */
    base->CC_R31 = 0U;                      /* set LR = 0 to signal a host task */
    base->CC_PC = CAU3_TASK_SHA256_UPDATE;  /* call cau_sha256_update() */
    base->CC_CMD = taskDone;                /* trigger cau3 execution */

    /* process the cau3 task completion signal specified by taskDone */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

/*!
 * @brief Initialize the SHA engine for new hash.
 *
 * This function sets NEW and MODE fields in SHA Control register to start new hash.
 *
 * @param base SHA peripheral base address.
 * @param ctxInternal Internal context.
 */
static status_t cau3_hash_engine_init(CAU3_Type *base, cau3_hash_ctx_internal_t *ctxInternal)
{
    status_t status;

    status = kStatus_InvalidArgument;

    if (kCAU3_Sha256 == ctxInternal->algo)
    {
        status = CAU3_Sha256InitializeOutput(base, ctxInternal->runningHash, kCAU3_TaskDonePoll);
    }

    if (kCAU3_Sha1 == ctxInternal->algo)
    {
        status = CAU3_Sha1InitializeOutput(base, ctxInternal->runningHash, kCAU3_TaskDonePoll);
    }

    return status;
}

/*!
 * @brief Adds message to current hash.
 *
 * This function merges the message to fill the internal buffer, empties the internal buffer if
 * it becomes full, then process all remaining message data.
 *
 *
 * @param base CAU3 peripheral base address.
 * @param ctxInternal Internal context.
 * @param message Input message.
 * @param messageSize Size of input message in bytes.
 * @return kStatus_Success.
 */
static status_t cau3_hash_process_message_data(CAU3_Type *base,
                                               cau3_hash_ctx_internal_t *ctxInternal,
                                               const uint8_t *message,
                                               size_t messageSize)
{
    status_t status;
    status_t (*funcUpdate)(CAU3_Type * cau3base, const uint8_t *msg, uint32_t numberOfBlocks, uint32_t *shaState,
                           cau3_task_done_t taskDone);

    /* first fill the internal buffer to full block */
    size_t toCopy = CAU3_HASH_BLOCK_SIZE - ctxInternal->blksz;
    cau3_memcpy(&ctxInternal->blk.b[ctxInternal->blksz], message, toCopy);
    message += toCopy;
    messageSize -= toCopy;

    status = kStatus_InvalidArgument;
    funcUpdate = NULL;

    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            funcUpdate = CAU3_Sha256Update;
            break;

        case kCAU3_Sha1:
            funcUpdate = CAU3_Sha1Update;
            break;

        default:
            break;
    }

    if (NULL != funcUpdate)
    {
        /* process full internal block */
        status = funcUpdate(base, &ctxInternal->blk.b[0], CAU3_HASH_BLOCK_SIZE / 64u, ctxInternal->runningHash,
                            kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }

        /* process all full blocks in message[] */
        while (messageSize >= CAU3_HASH_BLOCK_SIZE)
        {
            status =
                funcUpdate(base, message, CAU3_HASH_BLOCK_SIZE / 64u, ctxInternal->runningHash, kCAU3_TaskDonePoll);
            if (kStatus_Success != status)
            {
                return status;
            }
            message += CAU3_HASH_BLOCK_SIZE;
            messageSize -= CAU3_HASH_BLOCK_SIZE;
        }

        /* copy last incomplete message bytes into internal block */
        cau3_memcpy(&ctxInternal->blk.b[0], message, messageSize);
        ctxInternal->blksz = messageSize;
    }

    return status;
}

/*!
 * @brief Finalize the running hash to make digest.
 *
 * This function empties the internal buffer, adds padding bits, and generates final digest.
 *
 * @param base SHA peripheral base address.
 * @param ctxInternal Internal context.
 * @return kStatus_Success.
 */
static status_t cau3_hash_finalize(CAU3_Type *base, cau3_hash_ctx_internal_t *ctxInternal)
{
    cau3_sha_block_t lastBlock;
    status_t status;
    status_t (*funcUpdate)(CAU3_Type * cau3base, const uint8_t *msg, uint32_t numberOfBlocks, uint32_t *shaState,
                           cau3_task_done_t taskDone);

    status = kStatus_InvalidArgument;
    funcUpdate = NULL;

    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            funcUpdate = CAU3_Sha256Update;
            break;

        case kCAU3_Sha1:
            funcUpdate = CAU3_Sha1Update;
            break;

        default:
            break;
    }

    if (NULL == funcUpdate)
    {
        return kStatus_InvalidArgument;
    }

    memset(&lastBlock, 0, sizeof(cau3_sha_block_t));
    status = kStatus_Success;

    while (ctxInternal->blksz >= 64u)
    {
        status = funcUpdate(base, &ctxInternal->blk.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
        ctxInternal->blksz -= 64u;
        cau3_memcpy(&ctxInternal->blk.b[0], &ctxInternal->blk.b[64], ctxInternal->blksz);
    }

    /* this is last call, so need to flush buffered message bytes along with padding */
    if (ctxInternal->blksz <= 55u)
    {
        /* last data is 440 bits or less. */
        cau3_memcpy(&lastBlock.b[0], &ctxInternal->blk.b[0], ctxInternal->blksz);
        lastBlock.b[ctxInternal->blksz] = (uint8_t)0x80U;
        lastBlock.w[15] = __REV(8u * ctxInternal->fullMessageSize);
        status = funcUpdate(base, &lastBlock.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
    }
    else
    {
        if (ctxInternal->blksz < 64u)
        {
            ctxInternal->blk.b[ctxInternal->blksz] = (uint8_t)0x80U;
            for (uint32_t i = ctxInternal->blksz + 1u; i < 64u; i++)
            {
                ctxInternal->blk.b[i] = 0;
            }
        }
        else
        {
            lastBlock.b[0] = (uint8_t)0x80U;
        }

        status = funcUpdate(base, &ctxInternal->blk.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
        lastBlock.w[15] = __REV(8u * ctxInternal->fullMessageSize);
        status = funcUpdate(base, &lastBlock.b[0], 1, ctxInternal->runningHash, kCAU3_TaskDonePoll);
        if (kStatus_Success != status)
        {
            return status;
        }
    }
    return status;
}

status_t CAU3_HASH_Init(CAU3_Type *base, cau3_hash_ctx_t *ctx, cau3_hash_algo_t algo)
{
    status_t status;

    cau3_hash_ctx_internal_t *ctxInternal;
    /* compile time check for the correct structure size */
    BUILD_ASSURE(sizeof(cau3_hash_ctx_t) >= sizeof(cau3_hash_ctx_internal_t), cau3_hash_ctx_t_size);
    uint32_t i;

    status = cau3_hash_check_input_args(base, ctx, algo);
    if (status != kStatus_Success)
    {
        return status;
    }

    /* set algorithm in context struct for later use */
    ctxInternal = (cau3_hash_ctx_internal_t *)ctx;
    ctxInternal->algo = algo;
    ctxInternal->blksz = 0u;
    for (i = 0; i < sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); i++)
    {
        ctxInternal->blk.w[0] = 0u;
    }
    ctxInternal->state = kCAU3_StateHashInit;
    ctxInternal->fullMessageSize = 0;

    return status;
}

status_t CAU3_HASH_Update(CAU3_Type *base, cau3_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize)
{
    bool isUpdateState;
    status_t status;
    cau3_hash_ctx_internal_t *ctxInternal;
    size_t blockSize;

    if (inputSize == 0)
    {
        return kStatus_Success;
    }

    ctxInternal = (cau3_hash_ctx_internal_t *)ctx;
    status = cau3_hash_check_context(ctxInternal, input);
    if (kStatus_Success != status)
    {
        return status;
    }

    ctxInternal->fullMessageSize += inputSize;
    blockSize = CAU3_HASH_BLOCK_SIZE;
    /* if we are still less than CAU3_HASH_BLOCK_SIZE bytes, keep only in context */
    if ((ctxInternal->blksz + inputSize) <= blockSize)
    {
        cau3_memcpy((&ctxInternal->blk.b[0]) + ctxInternal->blksz, input, inputSize);
        ctxInternal->blksz += inputSize;
        return status;
    }
    else
    {
        isUpdateState = ctxInternal->state == kCAU3_StateHashUpdate;
        if (!isUpdateState)
        {
            /* start NEW hash */
            status = cau3_hash_engine_init(base, ctxInternal);
            if (status != kStatus_Success)
            {
                return status;
            }
            ctxInternal->state = kCAU3_StateHashUpdate;
        }
    }

    /* process input data */
    status = cau3_hash_process_message_data(base, ctxInternal, input, inputSize);
    return status;
}

status_t CAU3_HASH_Finish(CAU3_Type *base, cau3_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize)
{
    size_t algOutSize = 0;
    status_t status;
    cau3_hash_ctx_internal_t *ctxInternal;

    ctxInternal = (cau3_hash_ctx_internal_t *)ctx;
    status = cau3_hash_check_context(ctxInternal, output);
    if (kStatus_Success != status)
    {
        return status;
    }

    if (ctxInternal->state == kCAU3_StateHashInit)
    {
        status = cau3_hash_engine_init(base, ctxInternal);
        if (status != kStatus_Success)
        {
            return status;
        }
    }

    size_t outSize = 0u;

    /* compute algorithm output length */
    switch (ctxInternal->algo)
    {
        case kCAU3_Sha256:
            outSize = kCAU3_OutLenSha256;
            break;
        case kCAU3_Sha1:
            outSize = kCAU3_OutLenSha1;
            break;
        default:
            break;
    }
    algOutSize = outSize;

    /* flush message last incomplete block, if there is any, and add padding bits */
    status = cau3_hash_finalize(base, ctxInternal);

    if (outputSize)
    {
        if (algOutSize < *outputSize)
        {
            *outputSize = algOutSize;
        }
        else
        {
            algOutSize = *outputSize;
        }
    }

    cau3_memcpy(&output[0], ctxInternal->runningHash, algOutSize);

    memset(ctx, 0, sizeof(cau3_hash_ctx_t));
    return status;
}

status_t CAU3_HASH(
    CAU3_Type *base, cau3_hash_algo_t algo, const uint8_t *input, size_t inputSize, uint8_t *output, size_t *outputSize)
{
    cau3_hash_ctx_t hashCtx;
    status_t status;

    status = CAU3_HASH_Init(base, &hashCtx, algo);
    if (status != kStatus_Success)
    {
        return status;
    }

    status = CAU3_HASH_Update(base, &hashCtx, input, inputSize);
    if (status != kStatus_Success)
    {
        return status;
    }

    status = CAU3_HASH_Finish(base, &hashCtx, output, outputSize);

    return status;
}

/*! @brief CAU3 driver wait mechanism. */
status_t cau3_wait(CAU3_Type *base)
{
    status_t status;

    bool error = false;
    bool done = false;

    /* Wait for 'done' or 'error' flag. */
    while ((!error) && (!done))
    {
        uint32_t temp32 = base->STA;
        error = temp32 & kCAU3_StatusErrorIsr;
        done = temp32 & kCAU3_StatusDoneIsr;
    }

    if (error)
    {
        base->COM = CAU3_COM_ALL_MASK; /* Reset all engine to clear the error flag */
        status = kStatus_Fail;
    }
    else /* 'done' */
    {
        status = kStatus_Success;

        base->CW = kCAU3_ClearDataSize;
        /* Clear 'done' interrupt status.  This also clears the mode register. */
        base->STA = kCAU3_StatusDoneIsr;
    }

    return status;
}

/*!
 * @brief Clears the CAU3 module.
 * This function can be used to clear all sensitive data from theCAU3 module, such as private keys. It is called
 * internally by the CAU3 driver in case of an error or operation complete.
 * @param base CAU3 peripheral base address
 * @param pkha Include CAU3 PKHA register clear. If there is no PKHA, the argument is ignored.
 */
void cau3_clear_all(CAU3_Type *base, bool addPKHA)
{
    base->CW = (uint32_t)kCAU3_ClearAll;
    if (addPKHA)
    {
        cau3_pkha_clear_regabne(base, true, true, true, true);
    }
}

/*!
 * @brief Reads an unaligned word.
 *
 * This function creates a 32-bit word from an input array of four bytes.
 *
 * @param src Input array of four bytes. The array can start at any address in memory.
 * @return 32-bit unsigned int created from the input byte array.
 */
static inline uint32_t cau3_get_word_from_unaligned(const uint8_t *srcAddr)
{
#if (!(defined(__CORTEX_M)) || (defined(__CORTEX_M) && (__CORTEX_M == 0)))
    register const uint8_t *src = srcAddr;
    /* Cortex M0 does not support misaligned loads */
    if ((uint32_t)src & 0x3u)
    {
        union _align_bytes_t {
            uint32_t word;
            uint8_t byte[sizeof(uint32_t)];
        } my_bytes;

        my_bytes.byte[0] = *src;
        my_bytes.byte[1] = *(src + 1);
        my_bytes.byte[2] = *(src + 2);
        my_bytes.byte[3] = *(src + 3);
        return my_bytes.word;
    }
    else
    {
        /* addr aligned to 0-modulo-4 so it is safe to type cast */
        return *((const uint32_t *)src);
    }
#elif defined(__CC_ARM)
    /* -O3 optimization in Keil Compiler 5 uses LDM instruction here (LDM r4!, {r0})
     *    which is wrong, because srcAddr might be unaligned.
     *    LDM on unaligned address causes hard-fault. so use memcpy() */
    uint32_t ret;
    memcpy(&ret, srcAddr, sizeof(uint32_t));
    return ret;
#else
    return *((const uint32_t *)srcAddr);
#endif
}

/*******************************************************************************
 * PKHA Code static
 ******************************************************************************/

static status_t cau3_pkha_clear_regabne(CAU3_Type *base, bool A, bool B, bool N, bool E)
{
    cau3_mode_t mode;

    /* Set the PKHA algorithm and the appropriate function. */
    mode = (uint32_t)kCAU3_AlgorithmPKHA | 1U;

    /* Set ram area to clear. Clear all. */
    if (A)
    {
        mode |= 1U << 19U;
    }
    if (B)
    {
        mode |= 1U << 18U;
    }
    if (N)
    {
        mode |= 1U << 16U;
    }
    if (E)
    {
        mode |= 1U << 17U;
    }

    /* Write the mode register to the hardware.
     * NOTE: This will begin the operation. */
    base->MDPK = mode;

    /* Wait for 'done' */
    return cau3_wait(base);
}

static void cau3_pkha_default_parms(cau3_pkha_mode_params_t *params)
{
    params->func = (cau3_pkha_func_t)0;
    params->arithType = kCAU3_PKHA_IntegerArith;
    params->montFormIn = kCAU3_PKHA_NormalValue;
    params->montFormOut = kCAU3_PKHA_NormalValue;
    params->srcReg = kCAU3_PKHA_RegAll;
    params->srcQuad = kCAU3_PKHA_Quad0;
    params->dstReg = kCAU3_PKHA_RegAll;
    params->dstQuad = kCAU3_PKHA_Quad0;
    params->equalTime = kCAU3_PKHA_NoTimingEqualized;
    params->r2modn = kCAU3_PKHA_CalcR2;
}

static void cau3_pkha_write_word(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t index, uint32_t data)
{
    __IO uint32_t *pka = base->PKA0;
    __IO uint32_t *pkb = base->PKB0;
    __IO uint32_t *pkn = base->PKN0;

    switch (reg)
    {
        case kCAU3_PKHA_RegA:
            pka[index] = data;
            break;

        case kCAU3_PKHA_RegB:
            pkb[index] = data;
            break;

        case kCAU3_PKHA_RegN:
            pkn[index] = data;
            break;

        case kCAU3_PKHA_RegE:
            base->PKE[index] = data;
            break;

        default:
            break;
    }
}

static uint32_t cau3_pkha_read_word(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t index)
{
    uint32_t retval;
    __IO uint32_t *pka = base->PKA0;
    __IO uint32_t *pkb = base->PKB0;
    __IO uint32_t *pkn = base->PKN0;

    switch (reg)
    {
        case kCAU3_PKHA_RegA:
            retval = pka[index];
            break;

        case kCAU3_PKHA_RegB:
            retval = pkb[index];
            break;

        case kCAU3_PKHA_RegN:
            retval = pkn[index];
            break;

        default:
            retval = 0;
            break;
    }
    return retval;
}

static status_t cau3_pkha_write_reg(
    CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t quad, const uint8_t *data, size_t dataSize)
{
    /* Select the word-based start index for each quadrant of 128 bytes. */
    uint8_t startIndex = (quad * 32u);
    uint32_t outWord;

    while (dataSize > 0)
    {
        if (dataSize >= sizeof(uint32_t))
        {
            cau3_pkha_write_word(base, reg, startIndex++, cau3_get_word_from_unaligned(data));
            dataSize -= sizeof(uint32_t);
            data += sizeof(uint32_t);
        }
        else /* (dataSize > 0) && (dataSize < 4) */
        {
            outWord = 0;
            cau3_memcpy(&outWord, data, dataSize);
            cau3_pkha_write_word(base, reg, startIndex, outWord);
            dataSize = 0;
        }
    }

    return kStatus_Success;
}

static void cau3_pkha_read_reg(CAU3_Type *base, cau3_pkha_reg_area_t reg, uint8_t quad, uint8_t *data, size_t dataSize)
{
    /* Select the word-based start index for each quadrant of 128 bytes. */
    uint8_t startIndex = (quad * 32u);
    size_t calcSize;
    uint32_t word;

    while (dataSize > 0)
    {
        word = cau3_pkha_read_word(base, reg, startIndex++);

        calcSize = (dataSize >= sizeof(uint32_t)) ? sizeof(uint32_t) : dataSize;
        cau3_memcpy(data, &word, calcSize);

        data += calcSize;
        dataSize -= calcSize;
    }
}

static void cau3_pkha_init_data(CAU3_Type *base,
                                const uint8_t *A,
                                size_t sizeA,
                                const uint8_t *B,
                                size_t sizeB,
                                const uint8_t *N,
                                size_t sizeN,
                                const uint8_t *E,
                                size_t sizeE)
{
    uint32_t clearMask = kCAU3_ClearMode; /* clear Mode Register */

    /* Clear internal register states. */
    if (sizeA)
    {
        clearMask |= kCAU3_ClearPkhaSizeA;
    }
    if (sizeB)
    {
        clearMask |= kCAU3_ClearPkhaSizeB;
    }
    if (sizeN)
    {
        clearMask |= kCAU3_ClearPkhaSizeN;
    }
    if (sizeE)
    {
        clearMask |= kCAU3_ClearPkhaSizeE;
    }

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, A, B, N, E);

    /* Write register sizes. */
    /* Write modulus (N) and A and B register arguments. */
    if (sizeN)
    {
        base->PKNSZ = sizeN;
        if (N)
        {
            cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, sizeN);
        }
    }

    if (sizeA)
    {
        base->PKASZ = sizeA;
        if (A)
        {
            cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A, sizeA);
        }
    }

    if (sizeB)
    {
        base->PKBSZ = sizeB;
        if (B)
        {
            cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, B, sizeB);
        }
    }

    if (sizeE)
    {
        base->PKESZ = sizeE;
        if (E)
        {
            cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);
        }
    }
}

static void cau3_pkha_mode_set_src_reg_copy(cau3_mode_t *outMode, cau3_pkha_reg_area_t reg)
{
    int i = 0;

    do
    {
        reg = (cau3_pkha_reg_area_t)(((uint32_t)reg) >> 1u);
        i++;
    } while (reg);

    i = 4 - i;
    /* Source register must not be E. */
    if (i != 2)
    {
        *outMode |= ((uint32_t)i << 17u);
    }
}

static void cau3_pkha_mode_set_dst_reg_copy(cau3_mode_t *outMode, cau3_pkha_reg_area_t reg)
{
    int i = 0;

    do
    {
        reg = (cau3_pkha_reg_area_t)(((uint32_t)reg) >> 1u);
        i++;
    } while (reg);

    i = 4 - i;
    *outMode |= ((uint32_t)i << 10u);
}

static void cau3_pkha_mode_set_src_seg_copy(cau3_mode_t *outMode, const cau3_pkha_quad_area_t quad)
{
    *outMode |= ((uint32_t)quad << 8u);
}

static void cau3_pkha_mode_set_dst_seg_copy(cau3_mode_t *outMode, const cau3_pkha_quad_area_t quad)
{
    *outMode |= ((uint32_t)quad << 6u);
}

/*!
 * @brief Starts the PKHA operation.
 *
 * This function starts an operation configured by the params parameter.
 *
 * @param base CAU3 peripheral base address
 * @param params Configuration structure containing all settings required for PKHA operation.
 */
static status_t cau3_pkha_init_mode(CAU3_Type *base, const cau3_pkha_mode_params_t *params)
{
    cau3_mode_t modeReg;
    status_t retval;

    /* Set the PKHA algorithm and the appropriate function. */
    modeReg = kCAU3_AlgorithmPKHA;
    modeReg |= (uint32_t)params->func;

    if ((params->func == kCAU3_PKHA_CopyMemSizeN) || (params->func == kCAU3_PKHA_CopyMemSizeSrc))
    {
        /* Set source and destination registers and quads. */
        cau3_pkha_mode_set_src_reg_copy(&modeReg, params->srcReg);
        cau3_pkha_mode_set_dst_reg_copy(&modeReg, params->dstReg);
        cau3_pkha_mode_set_src_seg_copy(&modeReg, params->srcQuad);
        cau3_pkha_mode_set_dst_seg_copy(&modeReg, params->dstQuad);
    }
    else
    {
        /* Set the arithmetic type - integer or binary polynomial (F2m). */
        modeReg |= ((uint32_t)params->arithType << 17u);

        /* Set to use Montgomery form of inputs and/or outputs. */
        modeReg |= ((uint32_t)params->montFormIn << 19u);
        modeReg |= ((uint32_t)params->montFormOut << 18u);

        /* Set to use pre-computed R2modN */
        modeReg |= ((uint32_t)params->r2modn << 16u);
    }

    modeReg |= ((uint32_t)params->equalTime << 10u);

    /* Write the mode register to the hardware.
     * NOTE: This will begin the operation. */
    base->MDPK = modeReg;

    retval = cau3_wait(base);
    return (retval);
}

static status_t cau3_pkha_modR2(
    CAU3_Type *base, const uint8_t *N, size_t sizeN, uint8_t *result, size_t *resultSize, cau3_pkha_f2m_t arithType)
{
    status_t status;
    cau3_pkha_mode_params_t params;

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModR2;
    params.arithType = arithType;

    cau3_pkha_init_data(base, NULL, 0, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    return status;
}

static status_t cau3_pkha_modmul(CAU3_Type *base,
                                 const uint8_t *A,
                                 size_t sizeA,
                                 const uint8_t *B,
                                 size_t sizeB,
                                 const uint8_t *N,
                                 size_t sizeN,
                                 uint8_t *result,
                                 size_t *resultSize,
                                 cau3_pkha_f2m_t arithType,
                                 cau3_pkha_montgomery_form_t montIn,
                                 cau3_pkha_montgomery_form_t montOut,
                                 cau3_pkha_timing_t equalTime)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }

        if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModMul;
    params.arithType = arithType;
    params.montFormIn = montIn;
    params.montFormOut = montOut;
    params.equalTime = equalTime;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    return status;
}

/*******************************************************************************
 * PKHA Code public
 ******************************************************************************/
int CAU3_PKHA_CompareBigNum(const uint8_t *a, size_t sizeA, const uint8_t *b, size_t sizeB)
{
    int retval = 0;

    /* skip zero msbytes - integer a */
    while ((sizeA) && (0u == a[sizeA - 1]))
    {
        sizeA--;
    }

    /* skip zero msbytes - integer b */
    while ((sizeB) && (0u == b[sizeB - 1]))
    {
        sizeB--;
    }

    if (sizeA > sizeB)
    {
        retval = 1;
    } /* int a has more non-zero bytes, thus it is bigger than b */
    else if (sizeA < sizeB)
    {
        retval = -1;
    } /* int b has more non-zero bytes, thus it is bigger than a */
    else if (sizeA == 0)
    {
        retval = 0;
    } /* sizeA = sizeB = 0 */
    else
    {
        int n;
        int i;
        int val;
        uint32_t equal;

        n = sizeA - 1;
        i = 0;
        equal = 0;

        while (n >= 0)
        {
            uint32_t chXor = a[i] ^ b[i];

            equal |= chXor;
            val = (int)chXor * (a[i] - b[i]);

            if (val < 0)
            {
                retval = -1;
            }

            if (val > 0)
            {
                retval = 1;
            }

            if (val == 0)
            {
                val = 1;
            }

            if (val)
            {
                i++;
                n--;
            }
        }

        if (0 == equal)
        {
            retval = 0;
        }
    }
    return (retval);
}

status_t CAU3_PKHA_NormalToMontgomery(CAU3_Type *base,
                                      const uint8_t *N,
                                      size_t sizeN,
                                      uint8_t *A,
                                      size_t *sizeA,
                                      uint8_t *B,
                                      size_t *sizeB,
                                      uint8_t *R2,
                                      size_t *sizeR2,
                                      cau3_pkha_timing_t equalTime,
                                      cau3_pkha_f2m_t arithType)
{
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    /* need to convert our Integer inputs into Montgomery format */
    if (N && sizeN && R2 && sizeR2)
    {
        /* 1. R2 = MOD_R2(N) */
        status = cau3_pkha_modR2(base, N, sizeN, R2, sizeR2, arithType);
        if (status != kStatus_Success)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }

        /* 2. A(Montgomery) = MOD_MUL_IM_OM(A, R2, N) */
        if (A && sizeA)
        {
            status = cau3_pkha_modmul(base, A, *sizeA, R2, *sizeR2, N, sizeN, A, sizeA, arithType,
                                      kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
            if (status != kStatus_Success)
            {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
                cau3_release_semaphore(base);
#endif
                return status;
            }
        }

        /* 2. B(Montgomery) = MOD_MUL_IM_OM(B, R2, N) */
        if (B && sizeB)
        {
            status = cau3_pkha_modmul(base, B, *sizeB, R2, *sizeR2, N, sizeN, B, sizeB, arithType,
                                      kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
            if (status != kStatus_Success)
            {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
                cau3_release_semaphore(base);
#endif
                return status;
            }
        }

        cau3_clear_all(base, true);
    }
    else
    {
        status = kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_MontgomeryToNormal(CAU3_Type *base,
                                      const uint8_t *N,
                                      size_t sizeN,
                                      uint8_t *A,
                                      size_t *sizeA,
                                      uint8_t *B,
                                      size_t *sizeB,
                                      cau3_pkha_timing_t equalTime,
                                      cau3_pkha_f2m_t arithType)
{
    uint8_t one = 1;
    status_t status = kStatus_InvalidArgument;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    /* A = MOD_MUL_IM_OM(A(Montgomery), 1, N) */
    if (A && sizeA)
    {
        status = cau3_pkha_modmul(base, A, *sizeA, &one, sizeof(one), N, sizeN, A, sizeA, arithType,
                                  kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
        if (kStatus_Success != status)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }
    }

    /* B = MOD_MUL_IM_OM(B(Montgomery), 1, N) */
    if (B && sizeB)
    {
        status = cau3_pkha_modmul(base, B, *sizeB, &one, sizeof(one), N, sizeN, B, sizeB, arithType,
                                  kCAU3_PKHA_MontgomeryFormat, kCAU3_PKHA_MontgomeryFormat, equalTime);
        if (kStatus_Success != status)
        {
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
            cau3_release_semaphore(base);
#endif
            return status;
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModAdd(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *B,
                          size_t sizeB,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }

        if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModAdd;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModSub1(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *B,
                           size_t sizeB,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

    if (CAU3_PKHA_CompareBigNum(B, sizeB, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSub1;
    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModSub2(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *B,
                           size_t sizeB,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSub2;

    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModMul(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *B,
                          size_t sizeB,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType,
                          cau3_pkha_montgomery_form_t montIn,
                          cau3_pkha_montgomery_form_t montOut,
                          cau3_pkha_timing_t equalTime)
{
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    status =
        cau3_pkha_modmul(base, A, sizeA, B, sizeB, N, sizeN, result, resultSize, arithType, montIn, montOut, equalTime);

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModExp(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          const uint8_t *E,
                          size_t sizeE,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType,
                          cau3_pkha_montgomery_form_t montIn,
                          cau3_pkha_timing_t equalTime)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModExp;
    params.arithType = arithType;
    params.montFormIn = montIn;
    params.equalTime = equalTime;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, E, sizeE);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModSqrt(CAU3_Type *base,
                           const uint8_t *A,
                           size_t sizeA,
                           const uint8_t *N,
                           size_t sizeN,
                           uint8_t *result,
                           size_t *resultSize)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    /* A < N */
    if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
    {
        return (kStatus_InvalidArgument);
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModSqrt;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModRed(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModRed;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModInv(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

    /* A must be less than N -> CAU3_PKHA_CompareBigNum() must return -1 */
    if (arithType == kCAU3_PKHA_IntegerArith)
    {
        if (CAU3_PKHA_CompareBigNum(A, sizeA, N, sizeN) >= 0)
        {
            return (kStatus_InvalidArgument);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModInv;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModR2(
    CAU3_Type *base, const uint8_t *N, size_t sizeN, uint8_t *result, size_t *resultSize, cau3_pkha_f2m_t arithType)
{
    status_t status;
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif
    status = cau3_pkha_modR2(base, N, sizeN, result, resultSize, arithType);
    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModRR(
    CAU3_Type *base, const uint8_t *P, size_t sizeP, size_t sizeE, uint8_t *result, size_t *resultSize)
{
    status_t status;
    cau3_pkha_mode_params_t params;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithModRR;

    cau3_pkha_init_data(base, NULL, 0, NULL, 0, P, sizeP, NULL, sizeE);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ModGcd(CAU3_Type *base,
                          const uint8_t *A,
                          size_t sizeA,
                          const uint8_t *N,
                          size_t sizeN,
                          uint8_t *result,
                          size_t *resultSize,
                          cau3_pkha_f2m_t arithType)
{
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithGcd;
    params.arithType = arithType;

    cau3_pkha_init_data(base, A, sizeA, NULL, 0, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the result and size from register B0. */
        if (resultSize && result)
        {
            *resultSize = base->PKBSZ;
            /* Read the data from the result register into place. */
            cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, result, *resultSize);
        }
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_PrimalityTest(CAU3_Type *base,
                                 const uint8_t *A,
                                 size_t sizeA,
                                 const uint8_t *B,
                                 size_t sizeB,
                                 const uint8_t *N,
                                 size_t sizeN,
                                 bool *res)
{
    uint8_t result;
    cau3_pkha_mode_params_t params;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithPrimalityTest;
    cau3_pkha_init_data(base, A, sizeA, B, sizeB, N, sizeN, NULL, 0);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 0, &result, 1);

        *res = (bool)result;
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECC_PointAdd(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const cau3_pkha_ecc_point_t *B,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *bCurveParam,
                                size_t size,
                                cau3_pkha_f2m_t arithType,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEccAdd;
    params.arithType = arithType;
    params.r2modn = R2modN ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    if (R2modN)
    {
        cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 3, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECC_PointDouble(CAU3_Type *base,
                                   const cau3_pkha_ecc_point_t *B,
                                   const uint8_t *N,
                                   const uint8_t *aCurveParam,
                                   const uint8_t *bCurveParam,
                                   size_t size,
                                   cau3_pkha_f2m_t arithType,
                                   cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEccDouble;
    params.arithType = arithType;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECC_PointMul(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *bCurveParam,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                cau3_pkha_f2m_t arithType,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEccMul;
    params.equalTime = equalTime;
    params.arithType = arithType;
    params.r2modn = R2modN ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, true);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, bCurveParam, size);
    if (R2modN)
    {
        cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECM_PointMul(CAU3_Type *base,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *inputCoordinate,
                                const uint8_t *A24,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                uint8_t *outputCoordinate)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEcmMul;
    params.equalTime = equalTime;
    params.r2modn = R2modN ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, true);

    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, inputCoordinate, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, A24, size);

    if (R2modN)
    {
        base->PKBSZ = size;
        cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, outputCoordinate, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECT_PointMul(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const uint8_t *E,
                                size_t sizeE,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *dCurveParam,
                                size_t size,
                                cau3_pkha_timing_t equalTime,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEctMul;
    params.equalTime = equalTime;
    params.r2modn = R2modN ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, true);

    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKESZ = sizeE;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegE, 0, E, sizeE);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, dCurveParam, size);
    if (R2modN)
    {
        cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_PKHA_ECT_PointAdd(CAU3_Type *base,
                                const cau3_pkha_ecc_point_t *A,
                                const cau3_pkha_ecc_point_t *B,
                                const uint8_t *N,
                                const uint8_t *R2modN,
                                const uint8_t *aCurveParam,
                                const uint8_t *dCurveParam,
                                size_t size,
                                cau3_pkha_ecc_point_t *result)
{
    cau3_pkha_mode_params_t params;
    uint32_t clearMask;
    status_t status;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    status = cau3_lock_semaphore(base);
    if (kStatus_Success != status)
    {
        cau3_release_semaphore(base);
        return status;
    }
#endif

    cau3_pkha_default_parms(&params);
    params.func = kCAU3_PKHA_ArithEctAdd;
    params.r2modn = R2modN ? kCAU3_PKHA_InputR2 : kCAU3_PKHA_CalcR2;

    clearMask = kCAU3_ClearMode;

    /* Clear internal register states. */
    clearMask |= kCAU3_ClearPkhaSizeA;
    clearMask |= kCAU3_ClearPkhaSizeB;
    clearMask |= kCAU3_ClearPkhaSizeN;
    clearMask |= kCAU3_ClearPkhaSizeE;

    base->CW = clearMask;
    base->STA = kCAU3_StatusDoneIsr;
    cau3_pkha_clear_regabne(base, true, true, true, false);

    /* sizeN should be less than 64 bytes. */
    base->PKNSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegN, 0, N, size);

    base->PKASZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 0, A->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 1, A->Y, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegA, 3, aCurveParam, size);

    base->PKBSZ = size;
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 0, dCurveParam, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 1, B->X, size);
    cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 2, B->Y, size);
    if (R2modN)
    {
        cau3_pkha_write_reg(base, kCAU3_PKHA_RegB, 3, R2modN, size);
    }

    status = cau3_pkha_init_mode(base, &params);

    if (status == kStatus_Success)
    {
        /* Read the data from the result register into place. */
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 1, result->X, size);
        cau3_pkha_read_reg(base, kCAU3_PKHA_RegB, 2, result->Y, size);
    }

    cau3_clear_all(base, true);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return status;
}

status_t CAU3_TDES_CheckParity(CAU3_Type *base, cau3_key_slot_t keySlot)
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    taskDone = kCAU3_TaskDonePoll;

    /*  execute the cau3 "3des_check_parity" task  */
    base->CC_R[17] = keySlot;                  /*  keySlot  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_3DES_CHECK_PARITY; /*  call cau_3des_chk_parity()  */
    base->CC_CMD = taskDone;                   /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_TDES_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    /* only work with aligned key[] */
    if (0x3U & (uintptr_t)key)
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 24. */
    if (keySize != 24U)
    {
        return kStatus_InvalidArgument;
    }

    return cau3_load_key(base, key, keySize, handle->keySlot, handle->taskDone);
}

status_t CAU3_TDES_Encrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "3des_encrypt_ecb" task  */
    base->CC_R[16] = (uintptr_t)plaintext;  /*  pPlainText  */
    base->CC_R[17] = handle->keySlot;       /*  keySlot  */
    base->CC_R[19] = (uintptr_t)ciphertext; /*  pCipherText  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                      /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_3DES_ENCRYPT;   /*  call cau_3des_encrypt()  */
    base->CC_CMD = handle->taskDone;        /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_TDES_Decrypt(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext)
{
    status_t completionStatus;

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    /*  execute the cau3 "3des_decrypt_ecb" task  */
    base->CC_R[16] = (uintptr_t)ciphertext; /*  pCipherText  */
    base->CC_R[17] = handle->keySlot;       /*  keySlot  */
    base->CC_R[19] = (uintptr_t)plaintext;  /*  pPlainText  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                      /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_3DES_DECRYPT;   /*  call cau_3des_decrypt()  */
    base->CC_CMD = handle->taskDone;        /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_CHACHA20_POLY1305_SetKey(CAU3_Type *base, cau3_handle_t *handle, const uint8_t *key, size_t keySize)
{
    /* only work with aligned key[] */
    if (0x3U & (uintptr_t)key)
    {
        return kStatus_InvalidArgument;
    }

    /* keySize must be 32. */
    if (keySize != 32U)
    {
        return kStatus_InvalidArgument;
    }

    union {
        uint8_t b[32];
        uint32_t w[8];
    } tempKey;

    for (int i = 0; i < ARRAY_SIZE(tempKey.w); i++)
    {
        tempKey.w[i] = __REV(((const uint32_t *)(uintptr_t)key)[i]);
    }

    return cau3_load_key(base, tempKey.b, keySize, handle->keySlot, handle->taskDone);
}

static status_t cau3_load_nonce(CAU3_Type *base, const uint8_t *nonce, cau3_key_slot_t keySlot)
{
    union {
        uint8_t b[16];
        uint32_t w[4];
    } tempIv;

    memset(&tempIv, 0, sizeof(tempIv));

    /* set nonce to keySlot */
    cau3_memcpy(tempIv.b, nonce, 12);
    /* swap bytes */
    tempIv.w[0] = __REV(tempIv.w[0]);
    tempIv.w[1] = __REV(tempIv.w[1]);
    tempIv.w[2] = __REV(tempIv.w[2]);

    return CAU3_LoadKeyInitVector(base, tempIv.b, keySlot, kCAU3_TaskDonePoll);
}

status_t CAU3_CHACHA20_POLY1305_Encrypt(CAU3_Type *base,
                                        cau3_handle_t *handle,
                                        const uint8_t *plaintext,
                                        uint8_t *ciphertext,
                                        size_t size,
                                        const uint8_t *aad,
                                        size_t aadLen,
                                        const uint8_t *nonce,
                                        uint8_t *tag)
{
    status_t completionStatus;

    completionStatus = cau3_load_nonce(base, nonce, handle->keySlot);
    if (kStatus_Success != completionStatus)
    {
        return completionStatus;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    base->CC_R[17] = handle->keySlot;         /*  key/iv slot  */
    base->CC_R[18] = (uintptr_t)aad;          /*  AAD pointer  */
    base->CC_R[19] = aadLen;                  /*  AAD length (bytes)  */
    base->CC_R[20] = (uintptr_t)plaintext;    /*  Plaintext pointer  */
    base->CC_R[21] = size;                    /*  Plaintext length  */
    base->CC_R[22] = (uintptr_t)ciphertext;   /*  Ciphertext pointer  */
    base->CC_R[23] = (uintptr_t)tag;          /*  Tag pointer  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;        /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                        /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_CHA_POLY_ENCRYPT; /*  ChaChaPoly encrypt vector  */
    base->CC_CMD = handle->taskDone;          /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_CHACHA20_POLY1305_Decrypt(CAU3_Type *base,
                                        cau3_handle_t *handle,
                                        const uint8_t *ciphertext,
                                        uint8_t *plaintext,
                                        size_t size,
                                        const uint8_t *aad,
                                        size_t aadLen,
                                        const uint8_t *nonce,
                                        const uint8_t *tag)
{
    status_t completionStatus;

    completionStatus = cau3_load_nonce(base, nonce, handle->keySlot);
    if (kStatus_Success != completionStatus)
    {
        return completionStatus;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    base->CC_R[17] = handle->keySlot;         /*  key/iv slot  */
    base->CC_R[18] = (uintptr_t)aad;          /*  AAD pointer  */
    base->CC_R[19] = aadLen;                  /*  AAD length (bytes)  */
    base->CC_R[20] = (uintptr_t)ciphertext;   /*  Ciphertext pointer  */
    base->CC_R[21] = size;                    /*  Cyphertext length  */
    base->CC_R[22] = (uintptr_t)plaintext;    /*  Plaintext pointer  */
    base->CC_R[23] = (uintptr_t)tag;          /*  Tag pointer  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;        /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                        /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_CHA_POLY_DECRYPT; /*  ChaChaPoly decrypt vector  */
    base->CC_CMD = handle->taskDone;          /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, handle->taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_KeyBlobUnwrap(CAU3_Type *base,
                            cau3_key_slot_t keySlot,
                            const uint8_t *keyBlob,
                            uint32_t numberOfBlocks,
                            cau3_key_slot_t dstContext)
{
    status_t completionStatus;
    cau3_task_done_t taskDone;

    if (0x3U & (uintptr_t)keyBlob)
    {
        return kStatus_InvalidArgument;
    }

#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    completionStatus = cau3_lock_semaphore(base);
    if (kStatus_Success != completionStatus)
    {
        cau3_release_semaphore(base);
        return completionStatus;
    }
#endif

    taskDone = kCAU3_TaskDonePoll;

    /*  execute the cau3 "key blob unwrap" task  */
    base->CC_R[16] = (uintptr_t)keyBlob;     /*  pKeyBlob  */
    base->CC_R[17] = keySlot;                /*  keySlot  */
    base->CC_R[18] = numberOfBlocks;         /*  numberOfBlocks  */
    base->CC_R[19] = dstContext;             /*  destination key context  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;       /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                       /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_KEY_BLOB_UNWRAP; /*  call cau_key_blob_unwrap()  */
    base->CC_CMD = taskDone;                 /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_RPAtableInit(CAU3_Type *base, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    base->CC_R[16] = 1U;                    /*  initialize the RPA table  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                      /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_RPA_TABLE_SIZE; /*  call cau_rpa_insert_key()  */
    base->CC_CMD = taskDone;                /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_RPAtableSize(CAU3_Type *base, uint32_t *result)
{
    status_t completionStatus;

    /*  execute the cau3 "rpa table size" task  */
    base->CC_R[16] = 0U;                    /*  do not initialize the RPA table  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_PC = CAU3_TASK_RPA_TABLE_SIZE; /*  call cau_rpa_table_size  */

    /*  this is a simple/fast task, hence the task completion signal can be polled */
    base->CC_CMD = kCAU3_TaskDonePoll;
    completionStatus = cau3_process_task_completion(base, kCAU3_TaskDonePoll);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    *result = ((completionStatus == kStatus_Success) ? (uint32_t)base->CC_R[17] : 0);

    return (completionStatus);
}

status_t CAU3_RPAtableInsertKey(CAU3_Type *base,
                                const uint32_t *cauKey,
                                cau3_task_done_t taskDone)
{
    status_t completionStatus;

    base->CC_R[16] = (uintptr_t)cauKey;     /*  pointer to the key  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                      /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_RPA_INSERT_KEY; /*  call cau_rpa_insert_key()  */
    base->CC_CMD = taskDone;                /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);

}

status_t CAU3_RPAtableRemoveKey(CAU3_Type *base, uint32_t irkIx, cau3_task_done_t taskDone)
{
    status_t completionStatus;

    base->CC_R[16] = irkIx;                 /*  IRK index  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;      /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                      /*  set LR = 0 to signal a host task  */
    base->CC_PC = CAU3_TASK_RPA_REMOVE_KEY; /*  call cau_rpa_remove_key()  */
    base->CC_CMD = taskDone;                /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    return (completionStatus);
}

status_t CAU3_RPAtableSearch(CAU3_Type *base,
                             const uint32_t prand,
                             const uint32_t hash,
                             uint32_t *result,
                             cau3_task_done_t taskDone)
{
    status_t completionStatus;

    base->CC_R[16] = prand;                    /*  PRAND value  */
    base->CC_R[17] = hash;                     /*  hash  value  */
    base->CC_R30 = CAU3_DMEM_STK_BASE;         /*  initialize stack pointer (sp)  */
    base->CC_R31 = 0U;                         /*  set LR = 0 to signal a host task  */
    base->CC_PC =  CAU3_TASK_RPA_TABLE_SEARCH; /*  call cau_rpa_search()  */
    base->CC_CMD = taskDone;                   /*  trigger cau3 execution  */

    /*  process the cau3 task completion signal specified by taskDone  */
    completionStatus = cau3_process_task_completion(base, taskDone);
#if defined(FSL_CAU3_USE_HW_SEMA) && (FSL_CAU3_USE_HW_SEMA > 0)
    cau3_release_semaphore(base);
#endif
    *result = ((completionStatus == kStatus_Success) ? (uint32_t)base->CC_R[18] : 0xFF);

    return (completionStatus);
}
